@@ -34,7 +34,6 @@ import (
34
34
"regexp"
35
35
"strconv"
36
36
"strings"
37
- "sync"
38
37
39
38
"github.com/arduino/go-properties-orderedmap"
40
39
)
@@ -86,14 +85,13 @@ type ErrorCallback func(err string)
86
85
// it must be created using the NewDiscoveryServer function.
87
86
type DiscoveryServer struct {
88
87
impl Discovery
89
- out io.Writer
90
- outMutex sync.Mutex
88
+ outputChan chan * message
91
89
userAgent string
92
90
reqProtocolVersion int
93
91
initialized bool
94
92
started bool
95
93
syncStarted bool
96
- syncChannel chan interface {}
94
+ syncChannel chan * message
97
95
cachedPorts map [string ]* Port
98
96
cachedErr string
99
97
}
@@ -103,7 +101,8 @@ type DiscoveryServer struct {
103
101
// use the Run method.
104
102
func NewDiscoveryServer (impl Discovery ) * DiscoveryServer {
105
103
return & DiscoveryServer {
106
- impl : impl ,
104
+ impl : impl ,
105
+ outputChan : make (chan * message ),
107
106
}
108
107
}
109
108
@@ -113,20 +112,21 @@ func NewDiscoveryServer(impl Discovery) *DiscoveryServer {
113
112
// the input stream is closed. In case of IO error the error is
114
113
// returned.
115
114
func (d * DiscoveryServer ) Run (in io.Reader , out io.Writer ) error {
116
- d .out = out
115
+ go d .outputProcessor (out )
116
+ defer close (d .outputChan )
117
117
reader := bufio .NewReader (in )
118
118
for {
119
119
fullCmd , err := reader .ReadString ('\n' )
120
120
if err != nil {
121
- d .outputError ("command_error" , err .Error ())
121
+ d .outputChan <- messageError ("command_error" , err .Error ())
122
122
return err
123
123
}
124
124
fullCmd = strings .TrimSpace (fullCmd )
125
125
split := strings .Split (fullCmd , " " )
126
126
cmd := strings .ToUpper (split [0 ])
127
127
128
128
if ! d .initialized && cmd != "HELLO" && cmd != "QUIT" {
129
- d .outputError ("command_error" , fmt .Sprintf ("First command must be HELLO, but got '%s'" , cmd ))
129
+ d .outputChan <- messageError ("command_error" , fmt .Sprintf ("First command must be HELLO, but got '%s'" , cmd ))
130
130
continue
131
131
}
132
132
@@ -143,61 +143,61 @@ func (d *DiscoveryServer) Run(in io.Reader, out io.Writer) error {
143
143
d .stop ()
144
144
case "QUIT" :
145
145
d .impl .Quit ()
146
- d .outputOk ("quit" )
146
+ d .outputChan <- messageOk ("quit" )
147
147
return nil
148
148
default :
149
- d .outputError ("command_error" , fmt .Sprintf ("Command %s not supported" , cmd ))
149
+ d .outputChan <- messageError ("command_error" , fmt .Sprintf ("Command %s not supported" , cmd ))
150
150
}
151
151
}
152
152
}
153
153
154
154
func (d * DiscoveryServer ) hello (cmd string ) {
155
155
if d .initialized {
156
- d .outputError ("hello" , "HELLO already called" )
156
+ d .outputChan <- messageError ("hello" , "HELLO already called" )
157
157
return
158
158
}
159
159
re := regexp .MustCompile (`^(\d+) "([^"]+)"$` )
160
160
matches := re .FindStringSubmatch (cmd )
161
161
if len (matches ) != 3 {
162
- d .outputError ("hello" , "Invalid HELLO command" )
162
+ d .outputChan <- messageError ("hello" , "Invalid HELLO command" )
163
163
return
164
164
}
165
165
d .userAgent = matches [2 ]
166
166
if v , err := strconv .ParseInt (matches [1 ], 10 , 64 ); err != nil {
167
- d .outputError ("hello" , "Invalid protocol version: " + matches [2 ])
167
+ d .outputChan <- messageError ("hello" , "Invalid protocol version: " + matches [2 ])
168
168
return
169
169
} else {
170
170
d .reqProtocolVersion = int (v )
171
171
}
172
172
if err := d .impl .Hello (d .userAgent , 1 ); err != nil {
173
- d .outputError ("hello" , err .Error ())
173
+ d .outputChan <- messageError ("hello" , err .Error ())
174
174
return
175
175
}
176
- d .output ( & genericMessageJSON {
176
+ d .outputChan <- & message {
177
177
EventType : "hello" ,
178
178
ProtocolVersion : 1 , // Protocol version 1 is the only supported for now...
179
179
Message : "OK" ,
180
- })
180
+ }
181
181
d .initialized = true
182
182
}
183
183
184
184
func (d * DiscoveryServer ) start () {
185
185
if d .started {
186
- d .outputError ("start" , "Discovery already STARTed" )
186
+ d .outputChan <- messageError ("start" , "Discovery already STARTed" )
187
187
return
188
188
}
189
189
if d .syncStarted {
190
- d .outputError ("start" , "Discovery already START_SYNCed, cannot START" )
190
+ d .outputChan <- messageError ("start" , "Discovery already START_SYNCed, cannot START" )
191
191
return
192
192
}
193
193
d .cachedPorts = map [string ]* Port {}
194
194
d .cachedErr = ""
195
195
if err := d .impl .StartSync (d .eventCallback , d .errorCallback ); err != nil {
196
- d .outputError ("start" , "Cannot START: " + err .Error ())
196
+ d .outputChan <- messageError ("start" , "Cannot START: " + err .Error ())
197
197
return
198
198
}
199
199
d .started = true
200
- d .outputOk ("start" )
200
+ d .outputChan <- messageOk ("start" )
201
201
}
202
202
203
203
func (d * DiscoveryServer ) eventCallback (event string , port * Port ) {
@@ -216,65 +216,61 @@ func (d *DiscoveryServer) errorCallback(msg string) {
216
216
217
217
func (d * DiscoveryServer ) list () {
218
218
if ! d .started {
219
- d .outputError ("list" , "Discovery not STARTed" )
219
+ d .outputChan <- messageError ("list" , "Discovery not STARTed" )
220
220
return
221
221
}
222
222
if d .syncStarted {
223
- d .outputError ("list" , "discovery already START_SYNCed, LIST not allowed" )
223
+ d .outputChan <- messageError ("list" , "discovery already START_SYNCed, LIST not allowed" )
224
224
return
225
225
}
226
226
if d .cachedErr != "" {
227
- d .outputError ("list" , d .cachedErr )
227
+ d .outputChan <- messageError ("list" , d .cachedErr )
228
228
return
229
229
}
230
230
ports := []* Port {}
231
231
for _ , port := range d .cachedPorts {
232
232
ports = append (ports , port )
233
233
}
234
- type listOutputJSON struct {
235
- EventType string `json:"eventType"`
236
- Ports []* Port `json:"ports"`
237
- }
238
- d .output (& listOutputJSON {
234
+ d .outputChan <- & message {
239
235
EventType : "list" ,
240
- Ports : ports ,
241
- })
236
+ Ports : & ports ,
237
+ }
242
238
}
243
239
244
240
func (d * DiscoveryServer ) startSync () {
245
241
if d .syncStarted {
246
- d .outputError ("start_sync" , "Discovery already START_SYNCed" )
242
+ d .outputChan <- messageError ("start_sync" , "Discovery already START_SYNCed" )
247
243
return
248
244
}
249
245
if d .started {
250
- d .outputError ("start_sync" , "Discovery already STARTed, cannot START_SYNC" )
246
+ d .outputChan <- messageError ("start_sync" , "Discovery already STARTed, cannot START_SYNC" )
251
247
return
252
248
}
253
- c := make (chan interface {} , 10 ) // buffer up to 10 events
249
+ c := make (chan * message , 10 ) // buffer up to 10 events
254
250
d .syncChannel = c
255
251
if err := d .impl .StartSync (d .syncEvent , d .errorEvent ); err != nil {
256
- d .outputError ("start_sync" , "Cannot START_SYNC: " + err .Error ())
252
+ d .outputChan <- messageError ("start_sync" , "Cannot START_SYNC: " + err .Error ())
257
253
close (d .syncChannel ) // do not leak channel...
258
254
d .syncChannel = nil
259
255
return
260
256
}
261
257
d .syncStarted = true
262
- d .outputOk ("start_sync" )
258
+ d .outputChan <- messageOk ("start_sync" )
263
259
264
260
go func () {
265
261
for e := range c {
266
- d .output ( e )
262
+ d .outputChan <- e
267
263
}
268
264
}()
269
265
}
270
266
271
267
func (d * DiscoveryServer ) stop () {
272
268
if ! d .syncStarted && ! d .started {
273
- d .outputError ("stop" , "Discovery already STOPped" )
269
+ d .outputChan <- messageError ("stop" , "Discovery already STOPped" )
274
270
return
275
271
}
276
272
if err := d .impl .Stop (); err != nil {
277
- d .outputError ("stop" , "Cannot STOP: " + err .Error ())
273
+ d .outputChan <- messageError ("stop" , "Cannot STOP: " + err .Error ())
278
274
return
279
275
}
280
276
d .started = false
@@ -283,66 +279,31 @@ func (d *DiscoveryServer) stop() {
283
279
d .syncChannel = nil
284
280
d .syncStarted = false
285
281
}
286
- d .outputOk ("stop" )
282
+ d .outputChan <- messageOk ("stop" )
287
283
}
288
284
289
285
func (d * DiscoveryServer ) syncEvent (event string , port * Port ) {
290
- type syncOutputJSON struct {
291
- EventType string `json:"eventType"`
292
- Port * Port `json:"port"`
293
- }
294
- d .syncChannel <- & syncOutputJSON {
286
+ d .syncChannel <- & message {
295
287
EventType : event ,
296
288
Port : port ,
297
289
}
298
290
}
299
291
300
292
func (d * DiscoveryServer ) errorEvent (msg string ) {
301
- type syncOutputJSON struct {
302
- EventType string `json:"eventType"`
303
- Error bool `json:"error"`
304
- Message string `json:"message"`
305
- }
306
- d .syncChannel <- & syncOutputJSON {
307
- EventType : "start_sync" ,
308
- Error : true ,
309
- Message : msg ,
310
- }
311
- }
312
-
313
- type genericMessageJSON struct {
314
- EventType string `json:"eventType"`
315
- Message string `json:"message"`
316
- Error bool `json:"error,omitempty"`
317
- ProtocolVersion int `json:"protocolVersion,omitempty"`
293
+ d .syncChannel <- messageError ("start_sync" , msg )
318
294
}
319
295
320
- func (d * DiscoveryServer ) outputOk (event string ) {
321
- d .output (& genericMessageJSON {
322
- EventType : event ,
323
- Message : "OK" ,
324
- })
325
- }
326
-
327
- func (d * DiscoveryServer ) outputError (event , msg string ) {
328
- d .output (& genericMessageJSON {
329
- EventType : event ,
330
- Error : true ,
331
- Message : msg ,
332
- })
333
- }
334
-
335
- func (d * DiscoveryServer ) output (msg interface {}) {
336
- data , err := json .MarshalIndent (msg , "" , " " )
337
- if err != nil {
338
- d .output (& genericMessageJSON {
339
- EventType : "command_error" ,
340
- Error : true ,
341
- Message : err .Error (),
342
- })
343
- } else {
344
- d .outMutex .Lock ()
345
- fmt .Fprintln (d .out , string (data ))
346
- d .outMutex .Unlock ()
347
- }
296
+ func (d * DiscoveryServer ) outputProcessor (outWriter io.Writer ) {
297
+ // Start go routine to serialize messages printing
298
+ go func () {
299
+ for msg := range d .outputChan {
300
+ data , err := json .MarshalIndent (msg , "" , " " )
301
+ if err != nil {
302
+ // We are certain that this will be marshalled correctly
303
+ // so we don't handle the error
304
+ data , _ = json .MarshalIndent (messageError ("command_error" , err .Error ()), "" , " " )
305
+ }
306
+ fmt .Fprintln (outWriter , string (data ))
307
+ }
308
+ }()
348
309
}
0 commit comments