forked from Benzinga/go-bztcp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathproto.go
142 lines (118 loc) · 3.28 KB
/
proto.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package bztcp
import (
"bytes"
"encoding/json"
"fmt"
"time"
)
const (
// TimeFormat is how timestamps are expected to be formatted throughout most of
// the protocol.
TimeFormat = "Mon Jan _2 2006 15:04:05 GMT-0700 (MST)"
// EOL is the end-of-line magic for the BZ TCP protocol.
EOL = "=BZEOT\r\n"
// AuthTimeout is the amount of time spent waiting for authentication.
AuthTimeout = 10 * time.Second
// PingDuration is the amount of time between sending pings.
PingDuration = 20 * time.Second
)
var (
eol = []byte(EOL)
cdt = []byte(": ")
)
// Message is a raw message from the BZ TCP service.
type Message struct {
Status string
Data json.RawMessage
}
// AuthData is the message data sent in the AUTH message.
type AuthData struct {
Username string `json:"username"`
Key string `json:"key"`
}
// PingData is the message data sent in the PING message.
type PingData struct {
PingTime string `json:"pingTime"`
}
// PongData is the message data sent in the PONG message.
type PongData struct {
ServerTime string `json:"serverTime,omitempty"`
PingTime string `json:"pingTime"`
}
// Author represents an author
type Author struct {
Name string `json:"name"`
}
// TickerData contains a symbol associated with a STREAM message.
type TickerData struct {
Name string `json:"name"`
Extended bool `json:"-"`
Primary bool `json:"primary"`
Sentiment int `json:"sentiment"`
}
// Ticker is a special type that handles extended tickers.
type Ticker TickerData
// StreamData is the message data sent in the STREAM message.
type StreamData struct {
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
Authors []Author `json:"authors,omitempty"`
PublishedAt string `json:"published"`
UpdatedAt string `json:"updated"`
Channels []string `json:"channels"`
Tickers []Ticker `json:"tickers"`
Status string `json:"status"`
Link interface{} `json:"link"`
}
// UnmarshalJSON implements json.Unmarshaler
func (t *Ticker) UnmarshalJSON(b []byte) error {
switch b[0] {
case '{':
return json.Unmarshal(b, (*TickerData)(t))
case '"':
return json.Unmarshal(b, &t.Name)
default:
return fmt.Errorf("unexpected character '%c' in ticker", b[0])
}
}
// NewMessage creates a new messsage with the provided data.
func NewMessage(status string, body interface{}) (Message, error) {
msg := Message{Status: status}
if body != nil {
data, err := json.Marshal(body)
if err != nil {
return Message{}, err
}
msg.Data = data
}
return msg, nil
}
// Decode parses a message from bytes.
func (m *Message) Decode(line []byte) error {
colindex := bytes.IndexAny(line, ":=")
if colindex == -1 {
return fmt.Errorf("invalid line: %q", line)
}
m.Status = string(line[0:colindex])
equalidx := bytes.LastIndexByte(line, '=')
if colindex < equalidx {
m.Data = bytes.TrimSpace(line[colindex+1 : equalidx])
} else {
m.Data = nil
}
return nil
}
// Encode translates the message into bytes.
func (m *Message) Encode() []byte {
status, data := []byte(m.Status), []byte(m.Data)
buffer := bytes.Buffer{}
buffer.Grow(len(status) + len(data) + 10)
buffer.Write(status)
if m.Data != nil {
buffer.Write(cdt)
buffer.Write([]byte(data))
}
buffer.Write(eol)
return buffer.Bytes()
}