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.



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

def initialize(socket)
  @socket = socket
  @events = Hash.new
end

Instance Method Details

#dispatch_event(name, message) ⇒ Object



305
306
307
308
309
# File 'lib/vici.rb', line 305

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.



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/vici.rb', line 285

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
  if encoding.length == len
    return type, label, Message.new
  end
  return type, label, Message.new(encoding[len..-1])
end

#read_and_dispatch_eventObject



311
312
313
314
315
316
317
318
319
# File 'lib/vici.rb', line 311

def read_and_dispatch_event
  type, label, message = read
  p
  if type == EVENT
    dispatch_event(label, message)
  else
    raise TransportError, "unexpected message: #{type}"
  end
end

#read_and_dispatch_eventsObject



321
322
323
324
325
326
327
328
329
330
# File 'lib/vici.rb', line 321

def read_and_dispatch_events
  loop do
    type, label, message = read
    if type == EVENT
      dispatch_event(label, message)
    else
      return type, label, message
    end
  end
end

#recv_all(len) ⇒ Object

Receive data from socket, until len bytes read



247
248
249
250
251
252
253
254
255
256
257
# File 'lib/vici.rb', line 247

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

#register(name, handler) ⇒ Object

Register a handler method for the given event name



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/vici.rb', line 350

def register(name, handler)
  write(EVENT_REGISTER, name, nil)
  type, label, message = read_and_dispatch_events
  case type
    when EVENT_CONFIRM
      if @events.has_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.



335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/vici.rb', line 335

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



261
262
263
264
265
266
# File 'lib/vici.rb', line 261

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

#unregister(name, handler) ⇒ Object

Unregister a handler method for the given event name



369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/vici.rb', line 369

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.



271
272
273
274
275
276
277
278
279
280
# File 'lib/vici.rb', line 271

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