Code

ee47fcfcf0442d78e4a89f076491bc629dc427da
[sysdb/go.git] / proto / proto.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 proto provides helper functions for using the SysDB front-end
27 // protocol. That's the protocol used for communication between a client and a
28 // SysDB server instance.
29 package proto
31 import (
32         "encoding/binary"
33         "encoding/json"
34         "fmt"
35         "io"
36 )
38 // Network byte order.
39 var nbo = binary.BigEndian
41 // A Status represents the type of a message. The message type describes the
42 // current status or state of a connection depending on the context.
43 type Status uint32
45 const (
46         // ConnectionOK indicates that a command was successful.
47         ConnectionOK = Status(0)
48         // ConnectionError indicates that a command has failed.
49         ConnectionError = Status(1)
50         // ConnectionLog indicates an (asynchronous) log message.
51         ConnectionLog = Status(2)
53         // ConnectionData indicates a successful query returning data.
54         ConnectionData = Status(100)
55 )
57 const (
58         // ConnectionIdle is the internal state for idle connections.
59         ConnectionIdle = Status(0)
60         // ConnectionPing is the state requesting a connection check.
61         ConnectionPing = Status(1)
62         // ConnectionStartup is the state requesting the setup of a client
63         // connection.
64         ConnectionStartup = Status(2)
66         // ConnectionQuery is the state requesting the execution of a query in the
67         // server.
68         ConnectionQuery = Status(3)
69         // ConnectionFetch is the state requesting the execution of the 'FETCH'
70         // command in the server.
71         ConnectionFetch = Status(4)
72         // ConnectionList is the state requesting the execution of the 'LIST'
73         // command in the server.
74         ConnectionList = Status(5)
75         // ConnectionLookup is the state requesting the execution of the 'LOOKUP'
76         // command in the server.
77         ConnectionLookup = Status(6)
78         // ConnectionTimeseries is the state requesting the execution of the
79         // 'TIMESERIES' command in the server.
80         ConnectionTimeseries = Status(7)
82         // ConnectionMatcher is the internal state for parsing matchers.
83         ConnectionMatcher = Status(100)
84         // ConnectionExpr is the internal state for parsing expressions.
85         ConnectionExpr = Status(101)
86 )
88 // The DataType describes the type of data in a ConnectionData message.
89 type DataType int
91 const (
92         // A HostList can be unmarshaled to []sysdb.Host.
93         HostList DataType = iota
94         // A Host can be unmarshaled to sysdb.Host.
95         Host
96         // A Timeseries can be unmarshaled to sysdb.Timeseries.
97         Timeseries
98 )
100 // A Message represents a raw message of the SysDB front-end protocol.
101 type Message struct {
102         Type Status
103         Raw  []byte
106 // Read reads a raw message encoded in the SysDB wire format from r. The
107 // function parses the header but the raw body of the message will still be
108 // encoded in the wire format.
109 //
110 // The reader has to be in blocking mode. Otherwise, the client and server
111 // will be out of sync after reading a partial message and cannot recover from
112 // that.
113 func Read(r io.Reader) (*Message, error) {
114         var header [8]byte
115         if _, err := io.ReadFull(r, header[:]); err != nil {
116                 return nil, err
117         }
119         typ := nbo.Uint32(header[:4])
120         l := nbo.Uint32(header[4:])
121         msg := make([]byte, l)
122         if _, err := io.ReadFull(r, msg); err != nil {
123                 return nil, err
124         }
126         return &Message{Status(typ), msg}, nil
129 // Write writes a raw message to w. The raw body of m has to be encoded in the
130 // SysDB wire format. The function adds the right header to the message.
131 //
132 // The writer has to be in blocking mode. Otherwise, the client and server
133 // will be out of sync after writing a partial message and cannot recover from
134 // that.
135 func Write(w io.Writer, m *Message) error {
136         var header [8]byte
137         nbo.PutUint32(header[:4], uint32(m.Type))
138         nbo.PutUint32(header[4:], uint32(len(m.Raw)))
140         if _, err := io.WriteString(w, string(header[:])); err != nil {
141                 return err
142         }
143         if _, err := io.WriteString(w, string(m.Raw)); err != nil {
144                 return err
145         }
146         return nil
149 // DataType determines the type of data in a ConnectionData message.
150 func (m Message) DataType() (DataType, error) {
151         if m.Type != ConnectionData {
152                 return 0, fmt.Errorf("message is not of type DATA")
153         }
155         typ := nbo.Uint32(m.Raw[:4])
156         switch Status(typ) {
157         case ConnectionList, ConnectionLookup:
158                 return HostList, nil
159         case ConnectionFetch:
160                 return Host, nil
161         case ConnectionTimeseries:
162                 return Timeseries, nil
163         }
164         return 0, fmt.Errorf("unknown DATA type %d", typ)
167 // Unmarshal parses the raw body of m and stores the result in the value
168 // pointed to by v which has to match the type of the message and its data.
169 func Unmarshal(m *Message, v interface{}) error {
170         if m.Type != ConnectionData {
171                 return fmt.Errorf("unmarshaling message of type %d not supported", m.Type)
172         }
173         if len(m.Raw) == 0 { // empty command
174                 return nil
175         } else if len(m.Raw) < 4 {
176                 return fmt.Errorf("DATA message body too short")
177         }
178         return json.Unmarshal(m.Raw[4:], v)
181 // vim: set tw=78 sw=4 sw=4 noexpandtab :