Skip to content
This repository has been archived by the owner on Dec 7, 2018. It is now read-only.

Multi Protocol Server

//de edited this page Dec 12, 2013 · 5 revisions

Reel was first implemented solely as Reel::Server, implying the HTTP protocol. But SSL and UNIX servers were added and began to be widely used. This began to cause problems with what each initializer needed to begin a new server instance, but it gestured to the evolution of Reel into a multi-protocol server.

As Reel adapted, this caused:

  • Reel::Server to become Reel::Server::HTTP
  • Reel::SSLServer to become Reel::Server::SSL
  • The introduction of Reel::Server::UNIX

Simply changing the constant you use in your code as mentioned above will solve your broken code, after this major change. But in addition to breaking all previous code, this opens up an entirely new area in Reel. Now new protocols are easy to add to Reel.

###How To Add New Protocols

Observe what Server::HTTP does:

module Reel
  class Server
    class HTTP < Server

      # Create a new Reel HTTPS server
      #
      # @param [String] host address to bind to
      # @param [Fixnum] port to bind to
      # @option options [Fixnum] backlog of requests to accept
      #
      # @return [Reel::Server::HTTP] Reel HTTP server actor
      def initialize(host, port, options={}, &callback)
        optimize server = Celluloid::IO::TCPServer.new(host, port)
        options.merge!(host: host, port: port)
        super(server, options, &callback)
      end

    end
  end
end

Everything comes down to this line: super(server, options, &callback)

What you are doing when you are creating a new Reel::Server protocol, is creating an initializer which prepares a Celluloid::IO socket server. Unless something special is needed, Reel::Server provides you the methods run, shutdown, handle_connection and all the other methods involved in maintaining a socket. So compare the above with Server::SSL to see the contrast, and how certain methods can be over-ridden to provide special handling:

module Reel
  # Base class for Reel servers.
  #
  # This class is a Celluloid::IO actor which provides a barebones server
  # which does not open a socket itself, it just begin handling connections once
  # initialized with a specific kind of protocol-based server.

  # For specific protocol support, use:

  # Reel::Server::HTTP
  # Reel::Server::SSL
  # Reel::Server::SSL::UNIX

  class Server
    include Celluloid::IO
    # How many connections to backlog in the TCP accept queue
    DEFAULT_BACKLOG = 100

    execute_block_on_receiver :initialize
    finalizer :shutdown

    def initialize(server, options={}, &callback)
      @spy      = STDOUT if options[:spy]
      @options  = options
      @callback = callback
      @server   = server

      @server.listen(options.fetch(:backlog, DEFAULT_BACKLOG))

      async.run
    end

    def shutdown
      @server.close if @server
    end

    def run
      loop { async.handle_connection @server.accept }
    end

    def optimize(socket)
      if socket.is_a? TCPSocket
        socket.setsockopt(Socket::IPPROTO_TCP, :TCP_NODELAY, 1)
      end
    end

    def handle_connection(socket)
      if @spy
        require 'reel/spy'
        socket = Reel::Spy.new(socket, @spy)
      end

      connection = Connection.new(socket)

      begin
        @callback.call(connection)
      ensure
        if connection.attached?
          connection.close rescue nil
        end
      end
    rescue RequestError, EOFError
      # Client disconnected prematurely
      # TODO: log this?
    end
  end
end

Using the above examples, it is easy for you to add your own protocol handlers to Reel::Server. Simply write an initializer which invokes super properly, and if required by your case, over-ride the methods involved in maintaining connections with the protocol you are adding.

Clone this wiki locally