### Home Classes Methods

### In Files

- lib/phusion_passenger/message_channel.rb

### Parent

Object

### Namespace

- CLASS PhusionPassenger::MessageChannel::InvalidHashError

### Methods

- ::new
- #close
- #closed?
- #fileno
- #read
- #read_hash
- #read_scalar
- #recv_io
- #send_io
- #write
- #write_scalar

### Class/Module Index

Quicksearch

- PhusionPassenger
- PhusionPassenger::AbstractInstaller
- PhusionPassenger::AbstractInstaller::Abort
- PhusionPassenger::AbstractInstaller::CommandError
- PhusionPassenger::AdminTools
- PhusionPassenger::AdminTools::MemoryStats
- PhusionPassenger::AdminTools::MemoryStats::Process
- PhusionPassenger::AdminTools::ServerInstance
- PhusionPassenger::AdminTools::ServerInstance::CorruptedDirectoryError
- PhusionPassenger::AdminTools::ServerInstance::GenerationsAbsentError
- PhusionPassenger::AdminTools::ServerInstance::Group
- PhusionPassenger::AdminTools::ServerInstance::Process
- PhusionPassenger::AdminTools::ServerInstance::RoleDeniedError
- PhusionPassenger::AdminTools::ServerInstance::StaleDirectoryError
- PhusionPassenger::AdminTools::ServerInstance::Stats
- PhusionPassenger::AdminTools::ServerInstance::UnsupportedGenerationStructureVersionError
- PhusionPassenger::AnalyticsLogger
- PhusionPassenger::AnalyticsLogger::Connection
- PhusionPassenger::AnalyticsLogger::Lock
- PhusionPassenger::AnalyticsLogger::Log
- PhusionPassenger::ClassicRails
- PhusionPassenger::ClassicRails::CGIFixed
- PhusionPassenger::ClassicRails::ThreadHandlerExtension
- PhusionPassenger::ConsoleTextTemplate
- PhusionPassenger::DebugLogging
- PhusionPassenger::LoaderSharedHelpers
- PhusionPassenger::MessageChannel
- PhusionPassenger::MessageChannel::InvalidHashError
- PhusionPassenger::MessageClient
- PhusionPassenger::NativeSupportLoader
- PhusionPassenger::Packaging
- PhusionPassenger::PlatformInfo
- PhusionPassenger::PlatformInfo::Depcheck
- PhusionPassenger::PlatformInfo::Depcheck::ConsoleRunner
- PhusionPassenger::PlatformInfo::Depcheck::Dependency
- PhusionPassenger::PlatformInfo::RuntimeError
- PhusionPassenger::Plugin
- PhusionPassenger::PreloaderSharedHelpers
- PhusionPassenger::Rack
- PhusionPassenger::Rack::OutOfBandGc
- PhusionPassenger::Rack::ThreadHandlerExtension
- PhusionPassenger::Rails3Extensions
- PhusionPassenger::Rails3Extensions::AnalyticsLogging
- PhusionPassenger::Rails3Extensions::AnalyticsLogging::ACExtension
- PhusionPassenger::Rails3Extensions::AnalyticsLogging::ASBenchmarkableExtension
- PhusionPassenger::Rails3Extensions::AnalyticsLogging::ExceptionLogger
- PhusionPassenger::RequestHandler
- PhusionPassenger::RequestHandler::ThreadHandler
- PhusionPassenger::RequestHandler::ThreadHandler::Interrupted
- PhusionPassenger::SharedConstants
- PhusionPassenger::Standalone
- PhusionPassenger::Standalone::AppFinder
- PhusionPassenger::Standalone::Command
- PhusionPassenger::Standalone::ConfigFile
- PhusionPassenger::Standalone::ConfigFile::DisallowedContextError
- PhusionPassenger::Standalone::HelpCommand
- PhusionPassenger::Standalone::Main
- PhusionPassenger::Standalone::PackageRuntimeCommand
- PhusionPassenger::Standalone::RuntimeInstaller
- PhusionPassenger::Standalone::StartCommand
- PhusionPassenger::Standalone::StatusCommand
- PhusionPassenger::Standalone::StopCommand
- PhusionPassenger::Standalone::Utils
- PhusionPassenger::Standalone::VersionCommand
- PhusionPassenger::Utils
- PhusionPassenger::Utils::AnsiColors
- PhusionPassenger::Utils::FileSystemWatcher
- PhusionPassenger::Utils::FileSystemWatcher::DirInfo
- PhusionPassenger::Utils::FileSystemWatcher::FileInfo
- PhusionPassenger::Utils::HostsFileParser
- PhusionPassenger::Utils::TeeInput
- PhusionPassenger::Utils::TmpIO
- PhusionPassenger::Utils::UnseekableSocket
- CommonLibraryBuilder
- Exception
- GC
- IO
- Object
- Signal

No matching classes.

# PhusionPassenger::MessageChannel

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

### Attributes

io[RW]

The wrapped IO object.

### Public Class Methods

new(io = nil) click to toggle source

Create a new MessageChannel by wrapping the given IO object.

    # File lib/phusion_passenger/message_channel.rb, line 43
    def initialize(io = nil)
            @io = io
            # Make it binary just in case.
            @io.binmode if @io
    end

### Public Instance Methods

close() click to toggle source

Close the underlying IO stream. Might raise SystemCallError or IOError when something goes wrong.

    # File lib/phusion_passenger/message_channel.rb, line 315
    def close
            @io.close
    end

closed?() click to toggle source

Checks whether the underlying IO stream is closed.

    # File lib/phusion_passenger/message_channel.rb, line 320
    def closed?
            return @io.closed?
    end

fileno() click to toggle source

Return the file descriptor of the underlying IO object.

    # File lib/phusion_passenger/message_channel.rb, line 309
    def fileno
            return @io.fileno
    end

read() click to toggle source

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.

    # File lib/phusion_passenger/message_channel.rb, line 55
    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

read_hash() click to toggle source

Read an array message from the underlying file descriptor and return the result as a hash instead of an array. This assumes that the array message has an even number of elements. Returns nil when end-of-stream has been reached.

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

    # File lib/phusion_passenger/message_channel.rb, line 106
    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

read_scalar(buffer = new_buffer, max_size = nil) click to toggle source

Read a scalar message from the underlying IO object. Returns the read message, or nil on end-of-stream.

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

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

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

    # File lib/phusion_passenger/message_channel.rb, line 175
    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

recv_io(klass = IO, negotiate = true) click to toggle source

Receive an IO object (a file descriptor) from the channel. The other side must have sent an IO object by calling send_io(). Note that this only works on Unix sockets.

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

    # File lib/phusion_passenger/message_channel.rb, line 252
    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

send_io(io) click to toggle source

Send an IO object (a file descriptor) over the channel. The other side must receive the IO object by calling recv_io(). Note that this only works on Unix sockets.

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

    # File lib/phusion_passenger/message_channel.rb, line 265
    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

write(name, *args) click to toggle source

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.

    # File lib/phusion_passenger/message_channel.rb, line 223
    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) click to toggle source

Send a scalar message over the underlying IO object.

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

    # File lib/phusion_passenger/message_channel.rb, line 241
    def write_scalar(data)
            @io.write([data.size].pack('N') << data)
            @io.flush
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.