Code

Fixed compatibility issues with Go 1.0 and 1.1.
[sysdb/go.git] / client / conn.go
1 //
2 // Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the distribution.
13 //
14 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 // ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
18 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 package client
28 import (
29         "fmt"
30         "net"
31         "strings"
33         "github.com/sysdb/go/proto"
34 )
36 // A Conn is a connection to a SysDB server instance.
37 //
38 // Multiple goroutines may invoke methods on a Conn simultaneously but since
39 // the SysDB protocol requires a strict ordering of request and response
40 // messages, the communication with the server will usually happen
41 // sequentially.
42 type Conn struct {
43         c                   net.Conn
44         network, addr, user string
45 }
47 func (c *Conn) dial() (err error) {
48         if c.c, err = net.Dial(c.network, c.addr); err != nil {
49                 return err
50         }
51         defer func() {
52                 if err != nil {
53                         c.Close()
54                 }
55         }()
57         m := &proto.Message{
58                 Type: proto.ConnectionStartup,
59                 Raw:  []byte(c.user),
60         }
61         if err := c.Send(m); err != nil {
62                 return err
63         }
65         m, err = c.Receive()
66         if err != nil {
67                 return err
68         }
69         if m.Type == proto.ConnectionError {
70                 return fmt.Errorf("failed to startup session: %s", string(m.Raw))
71         }
72         if m.Type != proto.ConnectionOK {
73                 return fmt.Errorf("failed to startup session: unsupported")
74         }
75         return nil
76 }
78 // Dial sets up a client connection to a SysDB server instance at the
79 // specified address using the specified user.
80 //
81 // The address may be a UNIX domain socket, either prefixed with 'unix:' or
82 // specifying an absolute file-system path.
83 func Dial(addr, user string) (*Conn, error) {
84         network := "tcp"
85         if strings.HasPrefix(addr, "unix:") {
86                 network = "unix"
87                 addr = addr[len("unix:"):]
88         } else if len(addr) > 0 && addr[0] == '/' {
89                 network = "unix"
90         }
92         c := &Conn{network: network, addr: addr, user: user}
93         if err := c.dial(); err != nil {
94                 return nil, err
95         }
96         return c, nil
97 }
99 // Close closes the client connection.
100 //
101 // Any blocked Send or Receive operations will be unblocked and return errors.
102 func (c *Conn) Close() {
103         if c.c == nil {
104                 return
105         }
106         c.c.Close()
107         c.c = nil
110 // Send sends the specified raw message to the server.
111 //
112 // Send operations block until the full message could be written to the
113 // underlying sockets. This ensures that server and client don't get out of
114 // sync.
115 func (c *Conn) Send(m *proto.Message) error {
116         var err error
117         if c.c != nil {
118                 err = proto.Write(c.c, m)
119                 if err == nil {
120                         return nil
121                 }
122                 c.Close()
123         }
125         // Try to reconnect.
126         if e := c.dial(); e == nil {
127                 return proto.Write(c.c, m)
128         } else if err == nil {
129                 err = e
130         }
131         return err
134 // Receive waits for a reply from the server and returns the raw message.
135 //
136 // Receive operations block until a full message could be read from the
137 // underlying socket. This ensures that server and client don't get out of
138 // sync.
139 func (c *Conn) Receive() (*proto.Message, error) {
140         var err error
141         if c.c != nil {
142                 var m *proto.Message
143                 m, err = proto.Read(c.c)
144                 if err == nil {
145                         return m, err
146                 }
147                 c.Close()
148         }
150         // Try to reconnect.
151         if e := c.dial(); e == nil {
152                 return proto.Read(c.c)
153         } else if err == nil {
154                 err = e
155         }
156         return nil, err
159 // vim: set tw=78 sw=4 sw=4 noexpandtab :