-
Notifications
You must be signed in to change notification settings - Fork 87
Multi Protocol Server
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 becomeReel::Server::HTTP
-
Reel::SSLServer
to becomeReel::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.