class PhusionPassenger::MessageChannel

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

def check_argument(arg)

def check_argument(arg)
  if arg.to_s.index(DELIMITER)
    raise ArgumentError, "Message name and arguments may not contain #{DELIMITER_NAME}."
  end
end

def close

IOError when something goes wrong.
Close the underlying IO stream. Might raise SystemCallError or
def close
  @io.close
end

def closed?

Checks whether the underlying IO stream is closed.
def closed?
  return @io.closed?
end

def fileno

Return the file descriptor of the underlying IO object.
def fileno
  return @io.fileno
end

def initialize(io = nil)

Create a new MessageChannel by wrapping the given IO object.
def initialize(io = nil)
  @io = io
  # Make it binary just in case.
  @io.binmode if @io
end

def new_buffer

def new_buffer
  return ByteString.new
end

def new_buffer

def new_buffer
  return ""
end

def read

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

been reached.
Returns the array message as an array, or nil when end-of-stream has
Read an array message from the underlying file descriptor.
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
  return message
rescue Errno::ECONNRESET
  return nil
end

def read_hash

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

Returns nil when end-of-stream has been reached.
has an even number of elements.
result as a hash instead of an array. This assumes that the array message
Read an array message from the underlying file descriptor and return the
def read_hash
  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
  result = {}
  offset = 0
  delimiter_pos = buffer.index(DELIMITER, offset)
  while !delimiter_pos.nil?
    if delimiter_pos == 0
      name = ""
    else
      name = buffer[offset .. delimiter_pos - 1]
    end
    offset = delimiter_pos + 1
    delimiter_pos = buffer.index(DELIMITER, offset)
    if delimiter_pos.nil?
      raise InvalidHashError
    elsif delimiter_pos == 0
      value = ""
    else
      value = buffer[offset .. delimiter_pos - 1]
    end
    result[name] = value
    offset = delimiter_pos + 1
    delimiter_pos = buffer.index(DELIMITER, offset)
  end
  return result
rescue Errno::ECONNRESET
  return nil
end

def read_scalar(buffer = new_buffer, max_size = nil)

is larger than +max_size+, then a SecurityError will be raised.
size for the scalar message. If the received scalar message's size
The +max_size+ argument allows one to specify the maximum allowed

in order to minimize stress on the garbage collector.
stores the read data. It is good practice to reuse existing buffers
The +buffer+ argument specifies a buffer in which #read_scalar

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

read message, or nil on end-of-stream.
Read a scalar message from the underlying IO object. Returns the
def read_scalar(buffer = new_buffer, max_size = nil)
  if !@io.read(4, buffer)
    return nil
  end
  while buffer.size < 4
    tmp = @io.read(4 - buffer.size)
    if tmp.empty?
      return nil
    else
      buffer << tmp
    end
  end
  size = buffer.unpack(UINT32_PACK_FORMAT)[0]
  if size == 0
    buffer.replace('')
    return buffer
  else
    if !max_size.nil? && size > max_size
      raise SecurityError, "Scalar message size (#{size}) " <<
        "exceeds maximum allowed size (#{max_size})."
    end
    if !@io.read(size, buffer)
      return nil
    end
    if buffer.size < size
      tmp = ''
      while buffer.size < size
        if !@io.read(size - buffer.size, tmp)
          return nil
        else
          buffer << tmp
        end
      end
    end
    return buffer
  end
rescue Errno::ECONNRESET
  return nil
end

def recv_io(klass = IO, negotiate = true)

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

this only works on Unix sockets.
side must have sent an IO object by calling send_io(). Note that
Receive an IO object (a file descriptor) from the channel. The other
def recv_io(klass = IO, negotiate = true)
  write("pass IO") if negotiate
  io = @io.recv_io(klass)
  write("got IO") if negotiate
  return io
end

def send_io(io)

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

this only works on Unix sockets.
side must receive the IO object by calling recv_io(). Note that
Send an IO object (a file descriptor) over the channel. The other
def send_io(io)
  # We read a message before actually calling #send_io
  # in order to prevent the other side from accidentally
  # read()ing past the normal data and reading our file
  # descriptor too.
  #
  # For example suppose that side A looks like this:
  #
  #   read(fd, buf, 1024)
  #   read_io(fd)
  #
  # and side B:
  #
  #   write(fd, buf, 100)
  #   send_io(fd_to_pass)
  #
  # If B completes both write() and send_io(), then A's read() call
  # reads past the 100 bytes that B sent. On some platforms, like
  # Linux, this will cause read_io() to fail. And it just so happens
  # that Ruby's IO#read method slurps more than just the given amount
  # of bytes.
  result = read
  if !result
    raise EOFError, "End of stream"
  elsif result != ["pass IO"]
    raise IOError, "IO passing pre-negotiation header expected"
  else
    @io.send_io(io)
    # Once you've sent the IO you expect to be able to close it on the
    # sender's side, even if the other side hasn't read the IO yet.
    # Not so: on some operating systems (I'm looking at you OS X) this
    # can cause the receiving side to receive a bad file descriptor.
    # The post negotiation protocol ensures that we block until the
    # other side has really received the IO.
    result = read
    if !result
      raise EOFError, "End of stream"
    elsif result != ["got IO"]
      raise IOError, "IO passing post-negotiation header expected"
    end
  end
end

def write(name, *args)

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

to_s().
other elements. These arguments will internally be converted to strings by calling
file descriptor. _name_ is the first element in the message, and _args_ are the
Send an array message, which consists of the given elements, over the underlying
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

def write_scalar(data)

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

Send a scalar message over the underlying IO object.
def write_scalar(data)
  @io.write([data.size].pack('N') << data)
  @io.flush
end