Merge lp:~pedronis/ubuntu-push/try-hosts into lp:ubuntu-push/automatic

Proposed by Samuele Pedroni
Status: Merged
Approved by: Samuele Pedroni
Approved revision: 100
Merged at revision: 96
Proposed branch: lp:~pedronis/ubuntu-push/try-hosts
Merge into: lp:ubuntu-push/automatic
Diff against target: 613 lines (+377/-40)
2 files modified
client/session/session.go (+125/-26)
client/session/session_test.go (+252/-14)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/try-hosts
Reviewer Review Type Date Requested Status
John Lenton (community) Approve
Review via email: mp+213153@code.launchpad.net

This proposal supersedes a proposal from 2014-03-27.

Commit message

retrieves a list of hosts to try to connect to from a url (optionally for now)

Description of the change

optionally retrieves a list of hosts to try to connect to from a url,

still need changes in the client config and the interface between client and session: for a second branch, this one is already biggish

found that session.Close() has a race about Connection, will fix also in a later branch

To post a comment you must log in.
Revision history for this message
John Lenton (chipaca) :
review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (3.8 KiB)

The attempt to merge lp:~pedronis/ubuntu-push/try-hosts into lp:ubuntu-push/automatic failed. Below is the output from the failed tests.

mkdir -p /mnt/tarmac/cache/ubuntu-push/go-ws/bin
mkdir -p /mnt/tarmac/cache/ubuntu-push/go-ws/pkg
go get -u launchpad.net/godeps
go get -d -u launchpad.net/gocheck launchpad.net/go-dbus/v1 launchpad.net/go-xdg/v0 code.google.com/p/gosqlite/sqlite3
/mnt/tarmac/cache/ubuntu-push/go-ws/bin/godeps -u dependencies.tsv
"/mnt/tarmac/cache/ubuntu-push/go-ws/src/launchpad.net/gocheck" now at <email address hidden>
go install launchpad.net/gocheck launchpad.net/go-dbus/v1 launchpad.net/go-xdg/v0 code.google.com/p/gosqlite/sqlite3
go test launchpad.net/ubuntu-push/...
? launchpad.net/ubuntu-push [no test files]
ok launchpad.net/ubuntu-push/bus 0.021s
ok launchpad.net/ubuntu-push/bus/connectivity 1.142s
ok launchpad.net/ubuntu-push/bus/networkmanager 0.037s
ok launchpad.net/ubuntu-push/bus/notifications 0.016s
ok launchpad.net/ubuntu-push/bus/testing 0.017s
ok launchpad.net/ubuntu-push/bus/urldispatcher 0.021s
ok launchpad.net/ubuntu-push/client 0.099s
ok launchpad.net/ubuntu-push/client/gethosts 1.114s
ok launchpad.net/ubuntu-push/client/session 0.149s
ok launchpad.net/ubuntu-push/client/session/levelmap 0.060s
ok launchpad.net/ubuntu-push/config 0.008s
ok launchpad.net/ubuntu-push/external/murmur3 0.005s
--- FAIL: TestGoroutinesRunning (0.00 seconds)
 z_last_test.go:57: num goroutines = 1
 z_last_test.go:59: Too many goroutines.
 z_last_test.go:61: 1 instances of:
  syscall.Syscall(0x3, 0x9, 0x0, 0x0, 0x40bdaf, ...)
   /usr/lib/go/src/pkg/syscall/asm_linux_amd64.s:18 +0x5
  syscall.Close(0x9, 0x0, 0x0)
   /usr/lib/go/src/pkg/syscall/zsyscall_linux_amd64.go:267 +0x54
  net.closesocket(0x9, 0x2aceb7dbf848, 0x9)
   /usr/lib/go/src/pkg/net/fd_unix.go:474 +0x27
  net.(*netFD).destroy(0xc21128b230)
   /usr/lib/go/src/pkg/net/fd_unix.go:97 +0x45
  net.(*netFD).decref(0xc21128b230)
   /usr/lib/go/src/pkg/net/fd_unix.go:115 +0x47
  net.(*netFD).Close(0xc21128b230, 0x2acebb1e0f30, 0x0)
   /usr/lib/go/src/pkg/net/fd_unix.go:164 +0xa8
  net.(*conn).Close(0xc210077040, 0xc2110c0d80, 0xc210077040)
   /usr/lib/go/src/pkg/net/net.go:138 +0x95
  launchpad.net/ubuntu-push/http13client_test.funcĀ·083()
   /mnt/tarmac/cache/ubuntu-push/go-ws/src/launchpad.net/ubuntu-push/http13client/transport_test.go:1797 +0x13c
  created by launchpad.net/ubuntu-push/http13client_test.TestTransportTLSHandshakeTimeout
   /mnt/tarmac/cache/ubuntu-push/go-ws/src/launchpad.net/ubuntu-push/http13client/transport_test.go:1798 +0x22c
FAIL
FAIL launchpad.net/ubuntu-push/http13client 19.699s
ok launchpad.net/ubuntu-push/logger 0.006s
ok launchpad.net/ubuntu-push/protocol 0.010s
ok launchpad.net/ubuntu-push/server 0.062s
ok launchpad.net/ubuntu-push/server/acceptance 0.015s
? launchpad.net/ubuntu-push/server/acceptance/cmd [no test files]
? launchpad.net/ubuntu-push/server/acceptance/suites [no test files]
ok launchpad.net/ubuntu-push/server/api 0.022s
ok launchpad.net/ubuntu-push/server/broker 0.022s
ok launchpad.net/ubuntu-push/server/broker/simple 0.009s
? launchpad.net/ubuntu-pus...

Read more...

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (4.7 KiB)

The attempt to merge lp:~pedronis/ubuntu-push/try-hosts into lp:ubuntu-push/automatic failed. Below is the output from the failed tests.

mkdir -p /mnt/tarmac/cache/ubuntu-push/go-ws/bin
mkdir -p /mnt/tarmac/cache/ubuntu-push/go-ws/pkg
go get -u launchpad.net/godeps
go get -d -u launchpad.net/gocheck launchpad.net/go-dbus/v1 launchpad.net/go-xdg/v0 code.google.com/p/gosqlite/sqlite3
/mnt/tarmac/cache/ubuntu-push/go-ws/bin/godeps -u dependencies.tsv
"/mnt/tarmac/cache/ubuntu-push/go-ws/src/launchpad.net/gocheck" now at <email address hidden>
go install launchpad.net/gocheck launchpad.net/go-dbus/v1 launchpad.net/go-xdg/v0 code.google.com/p/gosqlite/sqlite3
go test launchpad.net/ubuntu-push/...
? launchpad.net/ubuntu-push [no test files]
ok launchpad.net/ubuntu-push/bus 0.010s
ok launchpad.net/ubuntu-push/bus/connectivity 1.142s
ok launchpad.net/ubuntu-push/bus/networkmanager 0.032s
ok launchpad.net/ubuntu-push/bus/notifications 0.016s
ok launchpad.net/ubuntu-push/bus/testing 0.017s
ok launchpad.net/ubuntu-push/bus/urldispatcher 0.015s
ok launchpad.net/ubuntu-push/client 0.055s
ok launchpad.net/ubuntu-push/client/gethosts 1.111s
ok launchpad.net/ubuntu-push/client/session 0.141s
ok launchpad.net/ubuntu-push/client/session/levelmap 0.049s
ok launchpad.net/ubuntu-push/config 0.008s
ok launchpad.net/ubuntu-push/external/murmur3 0.004s
ok launchpad.net/ubuntu-push/http13client 19.528s
ok launchpad.net/ubuntu-push/logger 0.007s
ok launchpad.net/ubuntu-push/protocol 0.013s
ok launchpad.net/ubuntu-push/server 0.042s
ok launchpad.net/ubuntu-push/server/acceptance 0.010s
? launchpad.net/ubuntu-push/server/acceptance/cmd [no test files]
? launchpad.net/ubuntu-push/server/acceptance/suites [no test files]
ok launchpad.net/ubuntu-push/server/api 0.017s
ok launchpad.net/ubuntu-push/server/broker 0.016s
ok launchpad.net/ubuntu-push/server/broker/simple 0.007s
? launchpad.net/ubuntu-push/server/broker/testing [no test files]
? launchpad.net/ubuntu-push/server/broker/testsuite [no test files]
? launchpad.net/ubuntu-push/server/dev [no test files]
ok launchpad.net/ubuntu-push/server/listener 0.388s
ok launchpad.net/ubuntu-push/server/session 0.160s
ok launchpad.net/ubuntu-push/server/store 0.218s
? launchpad.net/ubuntu-push/testing [no test files]
ok launchpad.net/ubuntu-push/testing/condition 0.008s
ok launchpad.net/ubuntu-push/util 0.009s
? launchpad.net/ubuntu-push/whoopsie [no test files]
ok launchpad.net/ubuntu-push/whoopsie/identifier 0.010s
ok launchpad.net/ubuntu-push/whoopsie/identifier/testing 0.010s
go test -race launchpad.net/ubuntu-push/...
? launchpad.net/ubuntu-push [no test files]
ok launchpad.net/ubuntu-push/bus 1.020s
ok launchpad.net/ubuntu-push/bus/connectivity 2.223s
ok launchpad.net/ubuntu-push/bus/networkmanager 1.050s
ok launchpad.net/ubuntu-push/bus/notifications 1.027s
ok launchpad.net/ubuntu-push/bus/testing 1.039s
ok launchpad.net/ubuntu-push/bus/urldispatcher 1.014s
ok launchpad.net/ubuntu-push/client 1.175s
ok launchpad.net/ubuntu-push/client/gethosts 2.153s
ok launchpad.net/ubuntu-push/client/session 1...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'client/session/session.go'
2--- client/session/session.go 2014-03-26 16:26:36 +0000
3+++ client/session/session.go 2014-03-28 14:47:06 +0000
4@@ -23,14 +23,17 @@
5 "crypto/x509"
6 "errors"
7 "fmt"
8+ "math/rand"
9+ "net"
10+ "strings"
11+ "sync/atomic"
12+ "time"
13+
14+ "launchpad.net/ubuntu-push/client/gethosts"
15 "launchpad.net/ubuntu-push/client/session/levelmap"
16 "launchpad.net/ubuntu-push/logger"
17 "launchpad.net/ubuntu-push/protocol"
18 "launchpad.net/ubuntu-push/util"
19- "math/rand"
20- "net"
21- "sync/atomic"
22- "time"
23 )
24
25 var wireVersionBytes = []byte{protocol.ProtocolWireVersion}
26@@ -45,6 +48,15 @@
27 protocol.NotificationsMsg
28 }
29
30+// parseServerAddrSpec recognizes whether spec is a HTTP URL to get
31+// hosts from or a |-separated list of host:port pairs.
32+func parseServerAddrSpec(spec string) (hostsEndpoint string, fallbackHosts []string) {
33+ if strings.HasPrefix(spec, "http") {
34+ return spec, nil
35+ }
36+ return "", strings.Split(spec, "|")
37+}
38+
39 // ClientSessionState is a way to broadly track the progress of the session
40 type ClientSessionState uint32
41
42@@ -56,14 +68,29 @@
43 Running
44 )
45
46+type hostGetter interface {
47+ Get() ([]string, error)
48+}
49+
50 // ClienSession holds a client<->server session and its configuration.
51 type ClientSession struct {
52 // configuration
53- DeviceId string
54- ServerAddr string
55- ExchangeTimeout time.Duration
56- Levels levelmap.LevelMap
57- Protocolator func(net.Conn) protocol.Protocol
58+ DeviceId string
59+ ExchangeTimeout time.Duration
60+ HostsCachingExpiryTime time.Duration
61+ ExpectAllRepairedTime time.Duration
62+ Levels levelmap.LevelMap
63+ Protocolator func(net.Conn) protocol.Protocol
64+ // hosts
65+ getHost hostGetter
66+ fallbackHosts []string
67+ deliveryHostsTimestamp time.Time
68+ deliveryHosts []string
69+ lastAttemptTimestamp time.Time
70+ leftToTry int
71+ tryHost int
72+ // hook for testing
73+ timeSince func(time.Time) time.Duration
74 // connection
75 Connection net.Conn
76 Log logger.Logger
77@@ -77,7 +104,7 @@
78 MsgCh chan *Notification
79 }
80
81-func NewSession(serverAddr string, pem []byte, exchangeTimeout time.Duration,
82+func NewSession(serverAddrSpec string, pem []byte, exchangeTimeout time.Duration,
83 deviceId string, levelmapFactory func() (levelmap.LevelMap, error),
84 log logger.Logger) (*ClientSession, error) {
85 state := uint32(Disconnected)
86@@ -85,15 +112,24 @@
87 if err != nil {
88 return nil, err
89 }
90+ var getHost hostGetter
91+ hostsEndpoint, fallbackHosts := parseServerAddrSpec(serverAddrSpec)
92+ if hostsEndpoint != "" {
93+ getHost = gethosts.New(deviceId, hostsEndpoint, exchangeTimeout)
94+ }
95 sess := &ClientSession{
96- ExchangeTimeout: exchangeTimeout,
97- ServerAddr: serverAddr,
98- DeviceId: deviceId,
99- Log: log,
100- Protocolator: protocol.NewProtocol0,
101- Levels: levels,
102- TLS: &tls.Config{InsecureSkipVerify: true}, // XXX
103- stateP: &state,
104+ ExchangeTimeout: exchangeTimeout,
105+ HostsCachingExpiryTime: 12 * time.Hour, // XXX take param
106+ ExpectAllRepairedTime: 30 * time.Minute, // XXX take param
107+ getHost: getHost,
108+ fallbackHosts: fallbackHosts,
109+ DeviceId: deviceId,
110+ Log: log,
111+ Protocolator: protocol.NewProtocol0,
112+ Levels: levels,
113+ TLS: &tls.Config{InsecureSkipVerify: true}, // XXX
114+ stateP: &state,
115+ timeSince: time.Since,
116 }
117 if pem != nil {
118 cp := x509.NewCertPool()
119@@ -114,13 +150,72 @@
120 atomic.StoreUint32(sess.stateP, uint32(state))
121 }
122
123+// getHosts sets deliverHosts possibly querying a remote endpoint
124+func (sess *ClientSession) getHosts() error {
125+ if sess.getHost != nil {
126+ if sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime {
127+ return nil
128+ }
129+ hosts, err := sess.getHost.Get()
130+ if err != nil {
131+ sess.Log.Errorf("getHosts: %v", err)
132+ sess.setState(Error)
133+ return err
134+ }
135+ sess.deliveryHostsTimestamp = time.Now()
136+ sess.deliveryHosts = hosts
137+ } else {
138+ sess.deliveryHosts = sess.fallbackHosts
139+ }
140+ return nil
141+}
142+
143+// startConnectionAttempt/nextHostToTry help connect iterating over candidate hosts
144+
145+func (sess *ClientSession) startConnectionAttempt() {
146+ if sess.timeSince(sess.lastAttemptTimestamp) > sess.ExpectAllRepairedTime {
147+ sess.tryHost = 0
148+ }
149+ sess.leftToTry = len(sess.deliveryHosts)
150+ sess.lastAttemptTimestamp = time.Now()
151+}
152+
153+func (sess *ClientSession) nextHostToTry() string {
154+ if sess.leftToTry == 0 {
155+ return ""
156+ }
157+ res := sess.deliveryHosts[sess.tryHost]
158+ sess.tryHost = (sess.tryHost + 1) % len(sess.deliveryHosts)
159+ sess.leftToTry--
160+ return res
161+}
162+
163+// we reached the Started state, we can retry with the same host if we
164+// have to retry again
165+func (sess *ClientSession) started() {
166+ sess.tryHost--
167+ if sess.tryHost == -1 {
168+ sess.tryHost = len(sess.deliveryHosts) - 1
169+ }
170+ sess.setState(Started)
171+}
172+
173 // connect to a server using the configuration in the ClientSession
174 // and set up the connection.
175 func (sess *ClientSession) connect() error {
176- conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout)
177- if err != nil {
178- sess.setState(Error)
179- return fmt.Errorf("connect: %s", err)
180+ sess.startConnectionAttempt()
181+ var err error
182+ var conn net.Conn
183+ for {
184+ host := sess.nextHostToTry()
185+ if host == "" {
186+ sess.setState(Error)
187+ return fmt.Errorf("connect: %s", err)
188+ }
189+ conn, err = net.DialTimeout("tcp", host, sess.ExchangeTimeout)
190+ if err == nil {
191+ break
192+ }
193 }
194 sess.Connection = tls.Client(conn, sess.TLS)
195 sess.setState(Connected)
196@@ -279,15 +374,19 @@
197 sess.proto = proto
198 sess.pingInterval = pingInterval
199 sess.Log.Debugf("Connected %v.", conn.LocalAddr())
200- sess.setState(Started)
201+ sess.started() // deals with choosing which host to retry with as well
202 return nil
203 }
204
205 // run calls connect, and if it works it calls start, and if it works
206 // it runs loop in a goroutine, and ships its return value over ErrCh.
207-func (sess *ClientSession) run(closer func(), connecter, starter, looper func() error) error {
208+func (sess *ClientSession) run(closer func(), hostGetter, connecter, starter, looper func() error) error {
209 closer()
210- err := connecter()
211+ err := hostGetter()
212+ if err != nil {
213+ return err
214+ }
215+ err = connecter()
216 if err == nil {
217 err = starter()
218 if err == nil {
219@@ -317,7 +416,7 @@
220 // keep on trying.
221 panic("can't Dial() without a protocol constructor.")
222 }
223- return sess.run(sess.doClose, sess.connect, sess.start, sess.loop)
224+ return sess.run(sess.doClose, sess.getHosts, sess.connect, sess.start, sess.loop)
225 }
226
227 func init() {
228
229=== modified file 'client/session/session_test.go'
230--- client/session/session_test.go 2014-03-27 13:26:10 +0000
231+++ client/session/session_test.go 2014-03-28 14:47:06 +0000
232@@ -23,16 +23,21 @@
233 "fmt"
234 "io"
235 "io/ioutil"
236+ "net"
237+ "net/http"
238+ "net/http/httptest"
239+ "reflect"
240+ "testing"
241+ "time"
242+
243 . "launchpad.net/gocheck"
244+
245 "launchpad.net/ubuntu-push/client/session/levelmap"
246+ //"launchpad.net/ubuntu-push/client/gethosts"
247 "launchpad.net/ubuntu-push/logger"
248 "launchpad.net/ubuntu-push/protocol"
249 helpers "launchpad.net/ubuntu-push/testing"
250 "launchpad.net/ubuntu-push/testing/condition"
251- "net"
252- "reflect"
253- "testing"
254- "time"
255 )
256
257 func TestSession(t *testing.T) { TestingT(t) }
258@@ -181,18 +186,43 @@
259 }
260
261 /****************************************************************
262+ parseServerAddrSpec() tests
263+****************************************************************/
264+
265+func (cs *clientSessionSuite) TestParseServerAddrSpec(c *C) {
266+ hEp, fallbackHosts := parseServerAddrSpec("http://foo/hosts")
267+ c.Check(hEp, Equals, "http://foo/hosts")
268+ c.Check(fallbackHosts, IsNil)
269+
270+ hEp, fallbackHosts = parseServerAddrSpec("foo:443")
271+ c.Check(hEp, Equals, "")
272+ c.Check(fallbackHosts, DeepEquals, []string{"foo:443"})
273+
274+ hEp, fallbackHosts = parseServerAddrSpec("foo:443|bar:443")
275+ c.Check(hEp, Equals, "")
276+ c.Check(fallbackHosts, DeepEquals, []string{"foo:443", "bar:443"})
277+}
278+
279+/****************************************************************
280 NewSession() tests
281 ****************************************************************/
282
283 func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) {
284- sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log)
285+ sess, err := NewSession("foo:443", nil, 0, "", cs.lvls, cs.log)
286 c.Check(sess, NotNil)
287 c.Check(err, IsNil)
288+ c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"})
289 // but no root CAs set
290 c.Check(sess.TLS.RootCAs, IsNil)
291 c.Check(sess.State(), Equals, Disconnected)
292 }
293
294+func (cs *clientSessionSuite) TestNewSessionHostEndpointWorks(c *C) {
295+ sess, err := NewSession("http://foo/hosts", pem, 0, "wah", cs.lvls, cs.log)
296+ c.Assert(err, IsNil)
297+ c.Check(sess.getHost, NotNil)
298+}
299+
300 var certfile string = helpers.SourceRelative("../../server/acceptance/config/testing.cert")
301 var pem, _ = ioutil.ReadFile(certfile)
302
303@@ -218,12 +248,141 @@
304 }
305
306 /****************************************************************
307+ getHosts() tests
308+****************************************************************/
309+
310+func (cs *clientSessionSuite) TestGetHostsFallback(c *C) {
311+ fallback := []string{"foo:443", "bar:443"}
312+ sess := &ClientSession{fallbackHosts: fallback}
313+ err := sess.getHosts()
314+ c.Assert(err, IsNil)
315+ c.Check(sess.deliveryHosts, DeepEquals, fallback)
316+}
317+
318+type testHostGetter struct {
319+ hosts []string
320+ err error
321+}
322+
323+func (thg *testHostGetter) Get() ([]string, error) {
324+ return thg.hosts, thg.err
325+}
326+
327+func (cs *clientSessionSuite) TestGetHostsRemote(c *C) {
328+ hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}
329+ sess := &ClientSession{getHost: hostGetter, timeSince: time.Since}
330+ err := sess.getHosts()
331+ c.Assert(err, IsNil)
332+ c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"})
333+}
334+
335+func (cs *clientSessionSuite) TestGetHostsRemoteError(c *C) {
336+ sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log)
337+ c.Assert(err, IsNil)
338+ hostsErr := errors.New("failed")
339+ hostGetter := &testHostGetter{nil, hostsErr}
340+ sess.getHost = hostGetter
341+ err = sess.getHosts()
342+ c.Assert(err, Equals, hostsErr)
343+ c.Check(sess.deliveryHosts, IsNil)
344+ c.Check(sess.State(), Equals, Error)
345+}
346+
347+func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) {
348+ hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}
349+ sess := &ClientSession{
350+ getHost: hostGetter,
351+ HostsCachingExpiryTime: 2 * time.Hour,
352+ timeSince: time.Since,
353+ }
354+ err := sess.getHosts()
355+ c.Assert(err, IsNil)
356+ hostGetter.hosts = []string{"baz:443"}
357+ // cached
358+ err = sess.getHosts()
359+ c.Assert(err, IsNil)
360+ c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"})
361+ // expired
362+ sess.timeSince = func(ts time.Time) time.Duration {
363+ return 3 * time.Hour
364+ }
365+ err = sess.getHosts()
366+ c.Assert(err, IsNil)
367+ c.Check(sess.deliveryHosts, DeepEquals, []string{"baz:443"})
368+}
369+
370+/****************************************************************
371+ startConnectionAttempt()/nextHostToTry()/started tests
372+****************************************************************/
373+
374+func (cs *clientSessionSuite) TestStartConnectionAttempt(c *C) {
375+ since := time.Since(time.Time{})
376+ sess := &ClientSession{
377+ ExpectAllRepairedTime: 10 * time.Second,
378+ timeSince: func(ts time.Time) time.Duration {
379+ return since
380+ },
381+ deliveryHosts: []string{"foo:443", "bar:443"},
382+ }
383+ // start from first host
384+ sess.startConnectionAttempt()
385+ c.Check(sess.lastAttemptTimestamp, Not(Equals), 0)
386+ c.Check(sess.tryHost, Equals, 0)
387+ c.Check(sess.leftToTry, Equals, 2)
388+ since = 1 * time.Second
389+ sess.tryHost = 1
390+ // just continue
391+ sess.startConnectionAttempt()
392+ c.Check(sess.tryHost, Equals, 1)
393+ sess.tryHost = 2
394+}
395+
396+func (cs *clientSessionSuite) TestNextHostToTry(c *C) {
397+ sess := &ClientSession{
398+ deliveryHosts: []string{"foo:443", "bar:443", "baz:443"},
399+ tryHost: 0,
400+ leftToTry: 3,
401+ }
402+ c.Check(sess.nextHostToTry(), Equals, "foo:443")
403+ c.Check(sess.nextHostToTry(), Equals, "bar:443")
404+ c.Check(sess.nextHostToTry(), Equals, "baz:443")
405+ c.Check(sess.nextHostToTry(), Equals, "")
406+ c.Check(sess.nextHostToTry(), Equals, "")
407+ c.Check(sess.tryHost, Equals, 0)
408+
409+ sess.leftToTry = 3
410+ sess.tryHost = 1
411+ c.Check(sess.nextHostToTry(), Equals, "bar:443")
412+ c.Check(sess.nextHostToTry(), Equals, "baz:443")
413+ c.Check(sess.nextHostToTry(), Equals, "foo:443")
414+ c.Check(sess.nextHostToTry(), Equals, "")
415+ c.Check(sess.nextHostToTry(), Equals, "")
416+ c.Check(sess.tryHost, Equals, 1)
417+}
418+
419+func (cs *clientSessionSuite) TestStarted(c *C) {
420+ sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log)
421+ c.Assert(err, IsNil)
422+
423+ sess.deliveryHosts = []string{"foo:443", "bar:443", "baz:443"}
424+ sess.tryHost = 1
425+
426+ sess.started()
427+ c.Check(sess.tryHost, Equals, 0)
428+ c.Check(sess.State(), Equals, Started)
429+
430+ sess.started()
431+ c.Check(sess.tryHost, Equals, 2)
432+}
433+
434+/****************************************************************
435 connect() tests
436 ****************************************************************/
437
438 func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) {
439 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
440 c.Assert(err, IsNil)
441+ sess.deliveryHosts = []string{"nowhere"}
442 err = sess.connect()
443 c.Check(err, ErrorMatches, ".*connect.*address.*")
444 c.Check(sess.State(), Equals, Error)
445@@ -233,12 +392,27 @@
446 srv, err := net.Listen("tcp", "localhost:0")
447 c.Assert(err, IsNil)
448 defer srv.Close()
449- sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log)
450- c.Assert(err, IsNil)
451- err = sess.connect()
452- c.Check(err, IsNil)
453- c.Check(sess.Connection, NotNil)
454- c.Check(sess.State(), Equals, Connected)
455+ sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
456+ c.Assert(err, IsNil)
457+ sess.deliveryHosts = []string{srv.Addr().String()}
458+ err = sess.connect()
459+ c.Check(err, IsNil)
460+ c.Check(sess.Connection, NotNil)
461+ c.Check(sess.State(), Equals, Connected)
462+}
463+
464+func (cs *clientSessionSuite) TestConnectSecondConnects(c *C) {
465+ srv, err := net.Listen("tcp", "localhost:0")
466+ c.Assert(err, IsNil)
467+ defer srv.Close()
468+ sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
469+ c.Assert(err, IsNil)
470+ sess.deliveryHosts = []string{"nowhere", srv.Addr().String()}
471+ err = sess.connect()
472+ c.Check(err, IsNil)
473+ c.Check(sess.Connection, NotNil)
474+ c.Check(sess.State(), Equals, Connected)
475+ c.Check(sess.tryHost, Equals, 0)
476 }
477
478 func (cs *clientSessionSuite) TestConnectConnectFail(c *C) {
479@@ -247,6 +421,7 @@
480 sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log)
481 srv.Close()
482 c.Assert(err, IsNil)
483+ sess.deliveryHosts = []string{srv.Addr().String()}
484 err = sess.connect()
485 c.Check(err, ErrorMatches, ".*connection refused")
486 c.Check(sess.State(), Equals, Error)
487@@ -688,20 +863,34 @@
488 run() tests
489 ****************************************************************/
490
491-func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) {
492+func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) {
493 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
494 c.Assert(err, IsNil)
495- failure := errors.New("TestRunBailsIfConnectFails")
496+ failure := errors.New("TestRunBailsIfHostGetterFails")
497 has_closed := false
498 err = sess.run(
499 func() { has_closed = true },
500 func() error { return failure },
501 nil,
502+ nil,
503 nil)
504 c.Check(err, Equals, failure)
505 c.Check(has_closed, Equals, true)
506 }
507
508+func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) {
509+ sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
510+ c.Assert(err, IsNil)
511+ failure := errors.New("TestRunBailsIfConnectFails")
512+ err = sess.run(
513+ func() {},
514+ func() error { return nil },
515+ func() error { return failure },
516+ nil,
517+ nil)
518+ c.Check(err, Equals, failure)
519+}
520+
521 func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) {
522 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
523 c.Assert(err, IsNil)
524@@ -709,6 +898,7 @@
525 err = sess.run(
526 func() {},
527 func() error { return nil },
528+ func() error { return nil },
529 func() error { return failure },
530 nil)
531 c.Check(err, Equals, failure)
532@@ -727,6 +917,7 @@
533 func() {},
534 func() error { return nil },
535 func() error { return nil },
536+ func() error { return nil },
537 func() error { sess.MsgCh <- notf; return <-failureCh })
538 c.Check(err, Equals, nil)
539 // if run doesn't error it sets up the channels
540@@ -794,7 +985,20 @@
541 timeout := 100 * time.Millisecond
542 lst, err := tls.Listen("tcp", "localhost:0", tlsCfg)
543 c.Assert(err, IsNil)
544- sess, err := NewSession(lst.Addr().String(), nil, timeout, "wah", cs.lvls, cs.log)
545+ // advertise
546+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
547+ b, err := json.Marshal(map[string]interface{}{
548+ "hosts": []string{"nowhere", lst.Addr().String()},
549+ })
550+ if err != nil {
551+ panic(err)
552+ }
553+ w.Header().Set("Content-Type", "application/json")
554+ w.Write(b)
555+ }))
556+ defer ts.Close()
557+
558+ sess, err := NewSession(ts.URL, nil, timeout, "wah", cs.lvls, cs.log)
559 c.Assert(err, IsNil)
560 tconn := &testConn{CloseCondition: condition.Fail2Work(10)}
561 sess.Connection = tconn
562@@ -823,6 +1027,9 @@
563 c.Assert(err, IsNil)
564 c.Assert(v, Equals, protocol.ProtocolWireVersion)
565
566+ // if something goes wrong session would try the first/other host
567+ c.Check(sess.tryHost, Equals, 0)
568+
569 // 2. "connect" (but on the fake protcol above! woo)
570
571 c.Check(takeNext(downCh), Equals, "deadline 100ms")
572@@ -843,6 +1050,9 @@
573 c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"})
574 upCh <- nil
575
576+ // session would retry the same host
577+ c.Check(sess.tryHost, Equals, 1)
578+
579 // and broadcasts...
580 b := &protocol.BroadcastMsg{
581 Type: "broadcast",
582@@ -870,3 +1080,31 @@
583 upCh <- failure
584 c.Check(<-sess.ErrCh, Equals, failure)
585 }
586+
587+func (cs *clientSessionSuite) TestDialWorksDirect(c *C) {
588+ // happy path thoughts
589+ cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)
590+ c.Assert(err, IsNil)
591+ tlsCfg := &tls.Config{
592+ Certificates: []tls.Certificate{cert},
593+ SessionTicketsDisabled: true,
594+ }
595+
596+ timeout := 100 * time.Millisecond
597+ lst, err := tls.Listen("tcp", "localhost:0", tlsCfg)
598+ c.Assert(err, IsNil)
599+ sess, err := NewSession(lst.Addr().String(), nil, timeout, "wah", cs.lvls, cs.log)
600+ c.Assert(err, IsNil)
601+ //defer sess.Close() xxx provokes a race, fix in a later branch
602+
603+ upCh := make(chan interface{}, 5)
604+ downCh := make(chan interface{}, 5)
605+ proto := &testProtocol{up: upCh, down: downCh}
606+ sess.Protocolator = func(net.Conn) protocol.Protocol { return proto }
607+
608+ go sess.Dial()
609+
610+ _, err = lst.Accept()
611+ c.Assert(err, IsNil)
612+ // connect done
613+}

Subscribers

People subscribed via source and target branches