class SpiderGazelle::Signaller

Attributes

gazelle[RW]
is_connected[R]
pipe[R]
shutting_down[R]
thread[R]

Public Class Methods

general_failure(_) click to toggle source
# File lib/spider-gazelle/signaller.rb, line 82
def self.general_failure(_)
    Reactor.instance.shutdown
end
new() click to toggle source
# File lib/spider-gazelle/signaller.rb, line 15
def initialize
    @thread = ::Libuv::Reactor.default
    @logger = Logger.instance

    # This is used to check if an instance of spider-gazelle is already running
    @is_master = false
    @is_client = false
    @is_connected  = false
    @shutting_down = false
    @client_check = @thread.defer
    @validated = [] # Set requires more processing
    @validating = {}
end

Public Instance Methods

authenticate(password) click to toggle source

Called from the spider process


# File lib/spider-gazelle/signaller.rb, line 71
def authenticate(password)
    @pipe.write "\x02validate #{password}\x03"
end
check() click to toggle source
# File lib/spider-gazelle/signaller.rb, line 37
def check
    @thread.next_tick do
        connect_to_sg_master
    end
    @client_check.promise
end
general_failure() click to toggle source
# File lib/spider-gazelle/signaller.rb, line 75
def general_failure
    @pipe.write "\x02Signaller general_failure\x03"
rescue
ensure
    Reactor.instance.shutdown
end
request(options) click to toggle source
# File lib/spider-gazelle/signaller.rb, line 29
def request(options)
    defer = @thread.defer

    defer.resolve(true)

    defer.promise
end
shutdown() click to toggle source
# File lib/spider-gazelle/signaller.rb, line 44
def shutdown
    defer = @thread.defer
    @shutting_down = true

    # Close the SIGNAL_SERVER pipe
    @pipe.close if @is_connected

    # Request spider or gazelle process to shutdown
    if @gazelle
        @gazelle.shutdown(defer)
    end

    if defined?(::SpiderGazelle::Spider)
        Spider.instance.shutdown(defer)
    else
        # This must be the master process
        @thread.next_tick do
            defer.resolve(true)
        end
    end

    defer.promise
end

Protected Instance Methods

become_sg_master() click to toggle source
# File lib/spider-gazelle/signaller.rb, line 130
def become_sg_master
    @is_master = true
    @is_client = false
    @is_connected = true

    # Load the server request processor here
    require 'spider-gazelle/signaller/signal_parser'
    @pipe = @thread.pipe :ipc

    begin
        File.unlink SIGNAL_SERVER
    rescue
    end

    @pipe.bind(SIGNAL_SERVER) do |client|
        @logger.verbose { "Client <0x#{client.object_id.to_s(16)}> connection made" }
        @validating[client.object_id] = SignalParser.new

        client.catch do |error|
            @logger.print_error(error, "Client <0x#{client.object_id.to_s(16)}> connection error")
        end

        client.finally do
            @validated.delete client
            @validating.delete client.object_id
            @logger.verbose { "Client <0x#{client.object_id.to_s(16)}> disconnected, #{@validated.length} remaining" }

            # If all the process connections are gone then we want to shutdown
            # This should never happen under normal conditions
            if @validated.length == 0
                Reactor.instance.shutdown unless @shutting_down
            end
        end

        client.progress do |data, client|
            process_request(data, client)
        end
        client.start_read
    end

    # catch server errors
    @pipe.catch { |error| panic! error }
    @pipe.finally { @is_connected = false }

    # start listening
    @pipe.listen(INTERNAL_PIPE_BACKLOG)
end
connect_to_sg_master() click to toggle source
# File lib/spider-gazelle/signaller.rb, line 90
def connect_to_sg_master
    @pipe = @thread.pipe :ipc
    @pipe.connect(SIGNAL_SERVER) do |client|
        @is_client = true
        @is_connected = true

        @logger.verbose "Client connected to SG Master"
        
        require 'uv-rays/buffered_tokenizer'
        @parser = ::UV::BufferedTokenizer.new({
            indicator: "\x02",
            delimiter: "\x03"
        })

        # The client processes responses here
        client.progress do |data, server|
            @parser.extract(data).each do |msg|
                Spider.instance.__send__(msg)
            end
        end
        client.start_read
        @client_check.resolve(true)
    end

    @pipe.catch do |reason|
        if !@is_client
            @client_check.resolve(false)
        end
    end

    @pipe.finally do
        if @is_client
            @is_connected = false
        else
            # Assume the role of master
            become_sg_master
        end
    end
end
panic!(reason) click to toggle source
# File lib/spider-gazelle/signaller.rb, line 178
def panic!(reason)
    #@logger.error "Master pipe went missing: #{reason}"
    # Server most likely exited
    # We'll shutdown here
    STDERR.puts "\n\npanic! #{reason.inspect}\n#{caller.join("\n")}\n\n\n"
    STDERR.flush
    Reactor.instance.shutdown
end
process_request(data, client) click to toggle source

The server processes requests here

# File lib/spider-gazelle/signaller.rb, line 188
def process_request(data, client)
    validated = @validated.include?(client)
    parser = @validating[client.object_id]

    if validated
        parser.process data
    else
        result = parser.signal(data)

        case result
        when :validated
            @validated.each do |old|
                old.write "\x02update\x03"
            end
            @validated << client
            if @validated.length > 1
                client.write "\x02wait\x03"
            else
                client.write "\x02ready\x03"
            end
            @logger.verbose { "Client <0x#{client.object_id.to_s(16)}> connection was validated" }
        when :close_connection
            client.close
            @logger.warn "Client <0x#{client.object_id.to_s(16)}> connection was closed due to bad credentials"
        end
    end
end