Class: Vici::Transport

Inherits:
Object
  • Object
show all
Defined in:
lib/vici.rb

Overview

The Transport class implements to low level segmentation of packets to the underlying transport stream. Directly using this class is usually not required.

Constant Summary collapse

CMD_REQUEST =
0
CMD_RESPONSE =
1
CMD_UNKNOWN =
2
EVENT_REGISTER =
3
EVENT_UNREGISTER =
4
EVENT_CONFIRM =
5
EVENT_UNKNOWN =
6
EVENT =
7

Instance Method Summary collapse

Constructor Details

#initialize(socket) ⇒ Transport

Create a transport layer using a provided socket for communication.



220
221
222
223
# File 'lib/vici.rb', line 220

def initialize(socket)
  @socket = socket
  @events = {}
end

Instance Method Details

#dispatch_event(name, message) ⇒ Object



279
280
281
282
283
# File 'lib/vici.rb', line 279

def dispatch_event(name, message)
  @events[name].each do |handler|
    handler.call(name, message)
  end
end

#readObject

Read a packet from the transport socket. Returns the packet type, and if available in the packet a label and the contained message.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/vici.rb', line 257

def read
  len = recv_all(4).unpack("N")[0]
  encoding = recv_all(len)
  type = encoding.unpack("c")[0]
  len = 1
  case type
  when CMD_REQUEST, EVENT_REGISTER, EVENT_UNREGISTER, EVENT
    label = encoding[2, encoding[1].unpack("c")[0]]
    len += label.length + 1
  when CMD_RESPONSE, CMD_UNKNOWN, EVENT_CONFIRM, EVENT_UNKNOWN
    label = nil
  else
    raise TransportError, "invalid message: #{type}"
  end
  message = if encoding.length == len
              Message.new
            else
              Message.new(encoding[len..-1])
            end
  [type, label, message]
end

#read_and_dispatch_eventObject

Raises:



285
286
287
288
289
290
# File 'lib/vici.rb', line 285

def read_and_dispatch_event
  type, label, message = read
  raise TransportError, "unexpected message: #{type}" if type != EVENT

  dispatch_event(label, message)
end

#read_and_dispatch_eventsObject



292
293
294
295
296
297
298
299
# File 'lib/vici.rb', line 292

def read_and_dispatch_events
  loop do
    type, label, message = read
    return type, label, message if type != EVENT

    dispatch_event(label, message)
  end
end

#recv_all(len) ⇒ Object

Receive data from socket, until len bytes read



227
228
229
230
231
232
233
234
235
# File 'lib/vici.rb', line 227

def recv_all(len)
  encoding = ""
  while encoding.length < len
    data = @socket.recv(len - encoding.length)
    raise TransportError, "connection closed" if data.empty?
    encoding << data
  end
  encoding
end

#register(name, handler) ⇒ Object

Register a handler method for the given event name



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/vici.rb', line 319

def register(name, handler)
  write(EVENT_REGISTER, name, nil)
  type, _label, _message = read_and_dispatch_events
  case type
  when EVENT_CONFIRM
    if @events.key?(name)
      @events[name] += [handler]
    else
      @events[name] = [handler]
    end
  when EVENT_UNKNOWN
    raise EventUnknownError, name
  else
    raise EventError, "invalid response for #{name} register"
  end
end

#request(name, message = nil) ⇒ Object

Send a command with a given name, and optionally a message. Returns the reply message on success.



304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/vici.rb', line 304

def request(name, message = nil)
  write(CMD_REQUEST, name, message)
  type, _label, message = read_and_dispatch_events
  case type
  when CMD_RESPONSE
    return message
  when CMD_UNKNOWN
    raise CommandUnknownError, name
  else
    raise CommandError, "invalid response for #{name}"
  end
end

#send_all(encoding) ⇒ Object

Send data to socket, until all bytes sent



239
240
241
242
# File 'lib/vici.rb', line 239

def send_all(encoding)
  len = 0
  len += @socket.send(encoding[len..-1], 0) while len < encoding.length
end

#unregister(name, handler) ⇒ Object

Unregister a handler method for the given event name



338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/vici.rb', line 338

def unregister(name, handler)
  write(EVENT_UNREGISTER, name, nil)
  type, _label, _message = read_and_dispatch_events
  case type
  when EVENT_CONFIRM
    @events[name] -= [handler]
  when EVENT_UNKNOWN
    raise EventUnknownError, name
  else
    raise EventError, "invalid response for #{name} unregister"
  end
end

#write(type, label, message) ⇒ Object

Write a packet prefixed by its length over the transport socket. Type specifies the message, the optional label and message get appended.



247
248
249
250
251
252
# File 'lib/vici.rb', line 247

def write(type, label, message)
  encoding = ""
  encoding << label.length << label if label
  encoding << message.encoding if message
  send_all([encoding.length + 1, type].pack("Nc") + encoding)
end