require 'active_support/core_ext/hash'
require 'msgpack/rpc'

module Serf

  ##
  # A MsgpackRpc handler that is just here to act as a protective
  # facade to only expose the 'call' rpc method to MsgpackRpc clients.
  #
  class MsgpackHandler
    def initialize(app, options={})
      @app = app
    end

    def call(env)
      @app.call env.stringify_keys
    end
  end

  ##
  # Defines a Msgpack RPC Server to run to receive messages.
  #
  class MsgpackReceiver
    DEFAULT_SERVER_TRANSPORT_CLASS = MessagePack::RPC::TCPServerTransport
    DEFAULT_ADDRESS_CLASS = MessagePack::RPC::Address

    def initialize(app, options={})
      @handler = options.fetch(:handler) { MsgpackHandler.new(app) }

      @listener = options.fetch(:listener) {
        host = options.fetch(:host) { '0.0.0.0' }
        port = options.fetch(:port) { 18800 }
        address = DEFAULT_ADDRESS_CLASS.new host, port
        DEFAULT_SERVER_TRANSPORT_CLASS.new address
      }

      @rpc_class = options.fetch(:rpc_class) { MessagePack::RPC::Server }
    end

    ##
    # Runs, doesn't return.
    def run
      svr = @rpc_class.new
      svr.listen @listener, @handler
      svr.run
    end

  end

end

require 'active_support/core_ext/hash'
require 'msgpack/rpc'

module Serf

  ##
  # A MsgpackRpc handler that is just here to act as a protective
  # facade to only expose the 'call' rpc method to MsgpackRpc clients.
  #
  class MsgpackHandler
    def initialize(app, options={})
      @app = app
      @logger = options.fetch(:logger) { ::Serf::NullObject.new }
    end

    def call(env)
      @app.call env.stringify_keys
    rescue => e
      @logger = options.fetch(:logger) { ::Serf::NullObject.new }
      # We reraise this error so it's passed on through to the remote client.
      raise e
    end
  end

  ##
  # Defines a Msgpack RPC Server to run to receive messages.
  #
  class MsgpackReceiver
    DEFAULT_SERVER_TRANSPORT_CLASS = MessagePack::RPC::TCPServerTransport
    DEFAULT_ADDRESS_CLASS = MessagePack::RPC::Address

    def initialize(app, options={})
      @handler = options.fetch(:handler) { MsgpackHandler.new(app) }

      @listener = options.fetch(:listener) {
        host = options.fetch(:host) { '0.0.0.0' }
        port = options.fetch(:port) { 18800 }
        address = DEFAULT_ADDRESS_CLASS.new host, port
        DEFAULT_SERVER_TRANSPORT_CLASS.new address
      }

      @rpc_class = options.fetch(:rpc_class) { MessagePack::RPC::Server }
    end

    ##
    # Runs, doesn't return.
    def run
      svr = @rpc_class.new
      svr.listen @listener, @handler
      svr.run
    end

  end

end

Add error logging and reraising of errors in msgpack receiver.
Add error logging and reraising of errors in msgpack receiver.

Details:
* Want some local logging of caught errors in msgpack receiver.
  So now both msgpack and redis_pubsub receivers will log an error
  if an exception is routed up to them.
* We are ok with reraising the error since msgpack rpc is good
  for us that way. Let the remote client know.
  This is for cases where we want handlers to return data
  directly to the client. This is the case of query commands.
