Class: UnionStationHooks::MessageChannel

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

Overview

This class allows reading and writing structured messages over I/O channels. This is the Ruby implementation of Passenger’s src/cxx_supportlib/Utils/MessageIO.h; see that file for more information.

Defined Under Namespace

Classes: InvalidHashError

Constant Summary collapse

HEADER_SIZE =
2
DELIMITER =
"\0"
DELIMITER_NAME =
'null byte'
UINT16_PACK_FORMAT =
'n'
UINT32_PACK_FORMAT =
'N'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io = nil) ⇒ MessageChannel

Create a new MessageChannel by wrapping the given IO object.



46
47
48
49
50
# File 'lib/union_station_hooks_core/message_channel.rb', line 46

def initialize(io = nil)
  @io = io
  # Make it binary just in case.
  @io.binmode if @io
end

Instance Attribute Details

#ioObject

The wrapped IO object.



43
44
45
# File 'lib/union_station_hooks_core/message_channel.rb', line 43

def io
  @io
end

Instance Method Details

#readObject

Read an array message from the underlying file descriptor. Returns the array message as an array, or nil when end-of-stream has been reached.

Might raise SystemCallError, IOError or SocketError when something goes wrong.



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
# File 'lib/union_station_hooks_core/message_channel.rb', line 61

def read
  buffer = new_buffer
  if !@io.read(HEADER_SIZE, buffer)
    return nil
  end
  while buffer.size < HEADER_SIZE
    tmp = @io.read(HEADER_SIZE - buffer.size)
    if tmp.empty?
      return nil
    else
      buffer << tmp
    end
  end

  chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0]
  if !@io.read(chunk_size, buffer)
    return nil
  end
  while buffer.size < chunk_size
    tmp = @io.read(chunk_size - buffer.size)
    if tmp.empty?
      return nil
    else
      buffer << tmp
    end
  end

  message = []
  offset = 0
  delimiter_pos = buffer.index(DELIMITER, offset)
  while !delimiter_pos.nil?
    if delimiter_pos == 0
      message << ''
    else
      message << buffer[offset..delimiter_pos - 1]
    end
    offset = delimiter_pos + 1
    delimiter_pos = buffer.index(DELIMITER, offset)
  end
  message
rescue Errno::ECONNRESET
  nil
end

#write(name, *args) ⇒ Object

Send an array message, which consists of the given elements, over the underlying file descriptor. name is the first element in the message, and args are the other elements. These arguments will internally be converted to strings by calling to_s().

Might raise SystemCallError, IOError or SocketError when something goes wrong.



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/union_station_hooks_core/message_channel.rb', line 115

def write(name, *args)
  check_argument(name)
  args.each do |arg|
    check_argument(arg)
  end

  message = "#{name}#{DELIMITER}"
  args.each do |arg|
    message << arg.to_s << DELIMITER
  end
  @io.write([message.size].pack('n') << message)
  @io.flush
end

#write_scalar(data) ⇒ Object

Send a scalar message over the underlying IO object.

Might raise SystemCallError, IOError or SocketError when something goes wrong.



133
134
135
136
# File 'lib/union_station_hooks_core/message_channel.rb', line 133

def write_scalar(data)
  @io.write([data.size].pack('N') << data)
  @io.flush
end