Merge lp:~chipaca/ubuntu-push/client-session into lp:ubuntu-push

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: no longer in the source branch.
Merged at revision: 34
Proposed branch: lp:~chipaca/ubuntu-push/client-session
Merge into: lp:ubuntu-push
Prerequisite: lp:~chipaca/ubuntu-push/levelmap
Diff against target: 317 lines (+308/-0)
2 files modified
client/session/session.go (+109/-0)
client/session/session_test.go (+199/-0)
To merge this branch: bzr merge lp:~chipaca/ubuntu-push/client-session
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+204104@code.launchpad.net

Commit message

A wild client session appears!

Description of the change

Client session, volume 1: Introduction to client session.

To post a comment you must log in.
Revision history for this message
Samuele Pedroni (pedronis) wrote :

21 +package session

missing package description

74 + return nil, errors.New("dial: could not parse certificate")

not in dial anymore

98 + sess.Connection = nil

does it make sense not do this in case of error?

Revision history for this message
Samuele Pedroni (pedronis) wrote :

106 + return errors.New("Can't run disconnected.")

+ return errors.New("Can't run without a protocol constructor.")

maybe they should start lowercase? there's chance they will be printed/logged: ...: err-msg

Revision history for this message
Samuele Pedroni (pedronis) wrote :

218 + badpem, _ := ioutil.ReadFile("/etc/passwd")

you can just make a []byte constant there, no?

Revision history for this message
Samuele Pedroni (pedronis) wrote :

237 + lp, err := net.Listen("tcp", ":0")

:0 doesn't mean localhost, I personally don't like tests opening port for the world, so localhost:0 would be better

what does lp stand for?

Revision history for this message
John Lenton (chipaca) wrote :

Added package description. Am still missing descriptions for a number of things.

Fixed the "dial:" thing. Uncapitalized the other things. Need to get used to the latter.

Nice catch wrt badpem. Fixed.

Not sure where I got "lp" from. Changed to "srv". There's probably a few more of these in down-pipe branches; I'll try to catch them now.

Revision history for this message
Samuele Pedroni (pedronis) wrote :

good

review: Approve
34. By John Lenton

[r=pedronis] A wild client session appears!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'client/session/session.go'
2--- client/session/session.go 1970-01-01 00:00:00 +0000
3+++ client/session/session.go 2014-01-31 12:44:37 +0000
4@@ -0,0 +1,109 @@
5+/*
6+ Copyright 2013-2014 Canonical Ltd.
7+
8+ This program is free software: you can redistribute it and/or modify it
9+ under the terms of the GNU General Public License version 3, as published
10+ by the Free Software Foundation.
11+
12+ This program is distributed in the hope that it will be useful, but
13+ WITHOUT ANY WARRANTY; without even the implied warranties of
14+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15+ PURPOSE. See the GNU General Public License for more details.
16+
17+ You should have received a copy of the GNU General Public License along
18+ with this program. If not, see <http://www.gnu.org/licenses/>.
19+*/
20+
21+// The client/session package handles the minutiae of interacting with
22+// the Ubuntu Push Notifications server.
23+package session
24+
25+import (
26+ "crypto/tls"
27+ "crypto/x509"
28+ "errors"
29+ "launchpad.net/ubuntu-push/client/session/levelmap"
30+ "launchpad.net/ubuntu-push/logger"
31+ "launchpad.net/ubuntu-push/protocol"
32+ "net"
33+ "time"
34+)
35+
36+var wireVersionBytes = []byte{protocol.ProtocolWireVersion}
37+
38+type Notification struct {
39+ // something something something
40+}
41+
42+// ClienSession holds a client<->server session and its configuration.
43+type ClientSession struct {
44+ // configuration
45+ DeviceId string
46+ ServerAddr string
47+ ExchangeTimeout time.Duration
48+ Levels levelmap.LevelMap
49+ Protocolator func(net.Conn) protocol.Protocol
50+ // connection
51+ Connection net.Conn
52+ Log logger.Logger
53+ TLS *tls.Config
54+ proto protocol.Protocol
55+ pingInterval time.Duration
56+ // status
57+ ErrCh chan error
58+ MsgCh chan *Notification
59+}
60+
61+func NewSession(serverAddr string, pem []byte, exchangeTimeout time.Duration,
62+ deviceId string, log logger.Logger) (*ClientSession, error) {
63+ sess := &ClientSession{
64+ ExchangeTimeout: exchangeTimeout,
65+ ServerAddr: serverAddr,
66+ DeviceId: deviceId,
67+ Log: log,
68+ Protocolator: protocol.NewProtocol0,
69+ Levels: levelmap.NewLevelMap(),
70+ TLS: &tls.Config{InsecureSkipVerify: true}, // XXX
71+ }
72+ if pem != nil {
73+ cp := x509.NewCertPool()
74+ ok := cp.AppendCertsFromPEM(pem)
75+ if !ok {
76+ return nil, errors.New("could not parse certificate")
77+ }
78+ sess.TLS.RootCAs = cp
79+ }
80+ return sess, nil
81+}
82+
83+// Dial connects to a server using the configuration in the ClientSession
84+// and sets up the connection.
85+func (sess *ClientSession) Dial() error {
86+ conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout)
87+ if err != nil {
88+ return err
89+ }
90+ sess.Connection = tls.Client(conn, sess.TLS)
91+ return nil
92+}
93+
94+func (sess *ClientSession) Close() {
95+ if sess.Connection != nil {
96+ sess.Connection.Close()
97+ // we ignore Close errors, on purpose (the thinking being that
98+ // the connection isn't really usable, and you've got nothing
99+ // you could do to recover at this stage).
100+ sess.Connection = nil
101+ }
102+}
103+
104+// call this to ensure the session is sane to run
105+func (sess *ClientSession) checkRunnable() error {
106+ if sess.Connection == nil {
107+ return errors.New("can't run disconnected.")
108+ }
109+ if sess.Protocolator == nil {
110+ return errors.New("can't run without a protocol constructor.")
111+ }
112+ return nil
113+}
114
115=== added file 'client/session/session_test.go'
116--- client/session/session_test.go 1970-01-01 00:00:00 +0000
117+++ client/session/session_test.go 2014-01-31 12:44:37 +0000
118@@ -0,0 +1,199 @@
119+/*
120+ Copyright 2013-2014 Canonical Ltd.
121+
122+ This program is free software: you can redistribute it and/or modify it
123+ under the terms of the GNU General Public License version 3, as published
124+ by the Free Software Foundation.
125+
126+ This program is distributed in the hope that it will be useful, but
127+ WITHOUT ANY WARRANTY; without even the implied warranties of
128+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
129+ PURPOSE. See the GNU General Public License for more details.
130+
131+ You should have received a copy of the GNU General Public License along
132+ with this program. If not, see <http://www.gnu.org/licenses/>.
133+*/
134+
135+package session
136+
137+import (
138+ "errors"
139+ "io/ioutil"
140+ . "launchpad.net/gocheck"
141+ "launchpad.net/ubuntu-push/logger"
142+ helpers "launchpad.net/ubuntu-push/testing"
143+ "launchpad.net/ubuntu-push/testing/condition"
144+ "net"
145+ "os"
146+ "testing"
147+ "time"
148+)
149+
150+func TestSession(t *testing.T) { TestingT(t) }
151+
152+type clientSessionSuite struct{}
153+
154+var nullog = logger.NewSimpleLogger(ioutil.Discard, "error")
155+var debuglog = logger.NewSimpleLogger(os.Stderr, "debug")
156+var _ = Suite(&clientSessionSuite{})
157+
158+//
159+// helpers! candidates to live in their own ../testing/ package.
160+//
161+
162+type xAddr string
163+
164+func (x xAddr) Network() string { return "<:>" }
165+func (x xAddr) String() string { return string(x) }
166+
167+// testConn (roughly based on the one in protocol_test)
168+
169+type testConn struct {
170+ Name string
171+ Deadlines []time.Duration
172+ Writes [][]byte
173+ WriteCondition condition.Interface
174+ DeadlineCondition condition.Interface
175+ CloseCondition condition.Interface
176+}
177+
178+func (tc *testConn) LocalAddr() net.Addr { return xAddr(tc.Name) }
179+
180+func (tc *testConn) RemoteAddr() net.Addr { return xAddr(tc.Name) }
181+
182+func (tc *testConn) Close() error {
183+ if tc.CloseCondition == nil || tc.CloseCondition.OK() {
184+ return nil
185+ } else {
186+ return errors.New("closer on fire")
187+ }
188+}
189+
190+func (tc *testConn) SetDeadline(t time.Time) error { panic("SetDeadline not implemented.") }
191+func (tc *testConn) SetReadDeadline(t time.Time) error { panic("SetReadDeadline not implemented.") }
192+func (tc *testConn) SetWriteDeadline(t time.Time) error { panic("SetWriteDeadline not implemented.") }
193+func (tc *testConn) Read(buf []byte) (n int, err error) { panic("Read not implemented.") }
194+func (tc *testConn) Write(buf []byte) (int, error) { panic("Write not implemented.") }
195+
196+/****************************************************************
197+ NewSession() tests
198+****************************************************************/
199+
200+func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) {
201+ sess, err := NewSession("", nil, 0, "", nullog)
202+ c.Check(sess, NotNil)
203+ c.Check(err, IsNil)
204+ // but no root CAs set
205+ c.Check(sess.TLS.RootCAs, IsNil)
206+}
207+
208+var certfile string = helpers.SourceRelative("../../server/acceptance/config/testing.cert")
209+var pem, _ = ioutil.ReadFile(certfile)
210+
211+func (cs *clientSessionSuite) TestNewSessionPEMWorks(c *C) {
212+ sess, err := NewSession("", pem, 0, "wah", nullog)
213+ c.Check(sess, NotNil)
214+ c.Assert(err, IsNil)
215+ c.Check(sess.TLS.RootCAs, NotNil)
216+}
217+
218+func (cs *clientSessionSuite) TestNewSessionBadPEMFileContentFails(c *C) {
219+ badpem := []byte("This is not the PEM you're looking for.")
220+ sess, err := NewSession("", badpem, 0, "wah", nullog)
221+ c.Check(sess, IsNil)
222+ c.Check(err, NotNil)
223+}
224+
225+/****************************************************************
226+ Dial() tests
227+****************************************************************/
228+
229+func (cs *clientSessionSuite) TestDialFailsWithNoAddress(c *C) {
230+ sess, err := NewSession("", nil, 0, "wah", debuglog)
231+ c.Assert(err, IsNil)
232+ err = sess.Dial()
233+ c.Assert(err, NotNil)
234+ c.Check(err.Error(), Matches, ".*dial.*address.*")
235+}
236+
237+func (cs *clientSessionSuite) TestDialConnects(c *C) {
238+ srv, err := net.Listen("tcp", "localhost:0")
239+ c.Assert(err, IsNil)
240+ defer srv.Close()
241+ sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", debuglog)
242+ c.Assert(err, IsNil)
243+ err = sess.Dial()
244+ c.Check(err, IsNil)
245+ c.Check(sess.Connection, NotNil)
246+}
247+
248+func (cs *clientSessionSuite) TestDialConnectFail(c *C) {
249+ srv, err := net.Listen("tcp", "localhost:0")
250+ c.Assert(err, IsNil)
251+ sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", debuglog)
252+ srv.Close()
253+ c.Assert(err, IsNil)
254+ err = sess.Dial()
255+ c.Check(sess.Connection, IsNil)
256+ c.Assert(err, NotNil)
257+ c.Check(err.Error(), Matches, ".*connection refused")
258+}
259+
260+/****************************************************************
261+ Close() tests
262+****************************************************************/
263+
264+func (cs *clientSessionSuite) TestClose(c *C) {
265+ sess, err := NewSession("", nil, 0, "wah", debuglog)
266+ c.Assert(err, IsNil)
267+ sess.Connection = &testConn{Name: "TestClose"}
268+ sess.Close()
269+ c.Check(sess.Connection, IsNil)
270+}
271+
272+func (cs *clientSessionSuite) TestCloseTwice(c *C) {
273+ sess, err := NewSession("", nil, 0, "wah", debuglog)
274+ c.Assert(err, IsNil)
275+ sess.Connection = &testConn{Name: "TestCloseTwice"}
276+ sess.Close()
277+ c.Check(sess.Connection, IsNil)
278+ sess.Close()
279+ c.Check(sess.Connection, IsNil)
280+}
281+
282+func (cs *clientSessionSuite) TestCloseFails(c *C) {
283+ sess, err := NewSession("", nil, 0, "wah", debuglog)
284+ c.Assert(err, IsNil)
285+ sess.Connection = &testConn{Name: "TestCloseFails", CloseCondition: condition.Work(false)}
286+ sess.Close()
287+ c.Check(sess.Connection, IsNil) // nothing you can do to clean up anyway
288+}
289+
290+/****************************************************************
291+ checkRunnable() tests
292+****************************************************************/
293+
294+func (cs *clientSessionSuite) TestCheckRunnableFailsIfNoConnection(c *C) {
295+ sess, err := NewSession("", nil, 0, "wah", debuglog)
296+ c.Assert(err, IsNil)
297+ // no connection!
298+ c.Check(sess.checkRunnable(), NotNil)
299+}
300+
301+func (cs *clientSessionSuite) TestCheckRunnableFailsIfNoProtocolator(c *C) {
302+ sess, err := NewSession("", nil, 0, "wah", debuglog)
303+ c.Assert(err, IsNil)
304+ // set up the connection
305+ sess.Connection = &testConn{}
306+ // And stomp on the protocolator
307+ sess.Protocolator = nil
308+ c.Check(sess.checkRunnable(), NotNil)
309+}
310+
311+func (cs *clientSessionSuite) TestCheckRunnable(c *C) {
312+ sess, err := NewSession("", nil, 0, "wah", debuglog)
313+ c.Assert(err, IsNil)
314+ // set up the connection
315+ sess.Connection = &testConn{}
316+ c.Check(sess.checkRunnable(), IsNil)
317+}

Subscribers

People subscribed via source and target branches