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

Commit

Permalink
Merge pull request #121 from decentrality/refactor-servers
Browse files Browse the repository at this point in the history
Refactor Reel::Server, refactor/retrofit/abandon SocketMixin, depending on #126 immediately hereafter.
  • Loading branch information
digitalextremist committed Dec 12, 2013
2 parents 99474a4 + a91f046 commit e28dad8
Show file tree
Hide file tree
Showing 22 changed files with 148 additions and 186 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ rvm:
- ruby-head
- jruby-19mode
- jruby-head
- rbx-19mode
- rbx

matrix:
allow_failures:
- rvm: rbx
- rvm: ruby-head
- rvm: jruby-head

Expand Down
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ group :development do
gem 'guard-rspec'
gem 'pry'
end

platforms :rbx do
gem 'racc'
gem 'rubinius-coverage'
gem 'rubysl', '~> 2.0'
end
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,25 @@

> "A dizzying lifetime... reeling by on celluloid" _-- Rush / Between The Wheels_
Reel is a fast, non-blocking "evented" web server built on [http_parser.rb][parser],
[websocket_parser][websockets], [Celluloid::IO][celluloidio], and [nio4r][nio4r]. Thanks
to Celluloid, Reel also works great for multithreaded applications and provides
traditional multithreaded blocking I/O support too.
Reel is a fast, non-blocking "evented" web server
built on [http_parser.rb][parser], [websocket_parser][websockets],
[Celluloid::IO][celluloidio], and [nio4r][nio4r]. Thanks to Celluloid,
Reel also works great for multithreaded applications
and provides traditional multithreaded blocking I/O support too.

[parser]: https://github.com/tmm1/http_parser.rb
[websockets]: https://github.com/afcapel/websocket_parser
[celluloidio]: https://github.com/celluloid/celluloid-io
[nio4r]: https://github.com/celluloid/nio4r

Connections to Reel can be either non-blocking and handled entirely within
the Reel::Server thread, or the same connections can be dispatched to worker
threads where they will perform ordinary blocking IO. Reel provides no
built-in thread pool, however you can build one yourself using Celluloid.pool,
or because Celluloid already pools threads to begin with, you can simply use
an actor per connection.
the Reel::Server thread (handling HTTP, SSL, or UNIX sockets),
or the same connections can be dispatched to worker threads
where they will perform ordinary blocking IO.
Reel provides no built-in thread pool,
however you can build one yourself using Celluloid.pool,
or because Celluloid already pools threads to begin with,
you can simply use an actor per connection.

This gives you the best of both worlds: non-blocking I/O for when you're
primarily I/O bound, and threads for where you're compute bound.
Expand Down Expand Up @@ -132,7 +135,7 @@ Reel lets you pass a block to initialize which receives connections:
```ruby
require 'reel'

Reel::Server.supervise("0.0.0.0", 3000) do |connection|
Reel::Server::HTTP.supervise("0.0.0.0", 3000) do |connection|
# Support multiple keep-alive requests per connection
connection.each_request do |request|
# WebSocket support
Expand Down Expand Up @@ -163,7 +166,7 @@ You can also subclass Reel, which allows additional customizations:
```ruby
require 'reel'

class MyServer < Reel::Server
class MyServer < Reel::Server::HTTP
def initialize(host = "127.0.0.1", port = 3000)
super(host, port, &method(:on_connection))
end
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/hello_reel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
addr, port = '127.0.0.1', 1234

puts "*** Starting server on #{addr}:#{port}"
Reel::Server.new(addr, port) do |connection|
Reel::Server::HTTP.new(addr, port) do |connection|
connection.respond :ok, "Hello World"
end

Expand Down
2 changes: 1 addition & 1 deletion benchmarks/reel_pool.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def handle_request(request)

connectionPool = MyConnectionHandler.pool

Reel::Server.run('127.0.0.1', 3000) do |connection|
Reel::Server::HTTP.run('127.0.0.1', 3000) do |connection|
# We're handing this connection off to another actor, so
# we detach it first before handing it off
connection.detach
Expand Down
25 changes: 0 additions & 25 deletions examples/chunked.rb

This file was deleted.

2 changes: 1 addition & 1 deletion examples/hello_world.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
addr, port = '127.0.0.1', 1234

puts "*** Starting server on http://#{addr}:#{port}"
Reel::Server.run(addr, port) do |connection|
Reel::Server::HTTP.run(addr, port) do |connection|
# For keep-alive support
connection.each_request do |request|
# Ordinarily we'd route the request here, e.g.
Expand Down
2 changes: 1 addition & 1 deletion examples/roundtrip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def new_message(topic)
end
end

class WebServer < Reel::Server
class WebServer < Reel::Server::HTTP
include Celluloid::Logger

def initialize(host = "0.0.0.0", port = 9000)
Expand Down
2 changes: 1 addition & 1 deletion examples/server_sent_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
require 'reel'


class ServerSentEvents < Reel::Server
class ServerSentEvents < Reel::Server::HTTP
include Celluloid::Logger

def initialize(ip = '127.0.0.1', port = 63310)
Expand Down
2 changes: 1 addition & 1 deletion examples/spy_hello_world.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
addr, port = '127.0.0.1', 1234

puts "*** Starting server on http://#{addr}:#{port}"
Reel::Server.run(addr, port, spy: true) do |connection|
Reel::Server::HTTP.run(addr, port, spy: true) do |connection|
# For keep-alive support
connection.each_request do |request|
# Ordinarily we'd route the request here, e.g.
Expand Down
2 changes: 1 addition & 1 deletion examples/ssl_hello_world.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
}

puts "*** Starting server on #{addr}:#{port}"
Reel::SSLServer.supervise(addr, port, options) do |connection|
Reel::Server::SSL.supervise(addr, port, options) do |connection|
# For keep-alive support
connection.each_request do |request|
# Ordinarily we'd route the request here, e.g.
Expand Down
6 changes: 3 additions & 3 deletions examples/websockets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def notify_time_change(topic, new_time)
end
end

class WebServer < Reel::Server
class WebServer < Reel::Server::HTTP
include Celluloid::Logger

def initialize(host = "127.0.0.1", port = 1234)
Expand All @@ -53,11 +53,11 @@ def on_connection(connection)

# We're going to hand off this connection to another actor (TimeClient)
# However, initially Reel::Connections are "attached" to the
# Reel::Server actor, meaning that the server manages the connection
# Reel::Server::HTTP actor, meaning that the server manages the connection
# lifecycle (e.g. error handling) for us.
#
# If we want to hand this connection off to another actor, we first
# need to detach it from the Reel::Server
# need to detach it from the Reel::Server (in this case, Reel::Server::HTTP)
connection.detach

route_websocket request.websocket
Expand Down
5 changes: 4 additions & 1 deletion lib/reel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
require 'reel/logger'
require 'reel/request'
require 'reel/response'

require 'reel/server'
require 'reel/ssl_server'
require 'reel/server/http'
require 'reel/server/ssl'

require 'reel/websocket'
require 'reel/stream'

Expand Down
4 changes: 3 additions & 1 deletion lib/reel/mixins.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def remote_host
# NOTE: Celluloid::IO does not yet support non-blocking reverse DNS
socket.peeraddr(true)[2]
end

end

module RequestMixin
Expand All @@ -31,7 +32,7 @@ def headers
@request_info.headers
end

def [] header
def [](header)
headers[header]
end

Expand Down Expand Up @@ -60,4 +61,5 @@ def fragment
end

end

end
77 changes: 23 additions & 54 deletions lib/reel/server.rb
Original file line number Diff line number Diff line change
@@ -1,71 +1,34 @@
module Reel
# The Reel HTTP server class
# Base class for Reel servers.
#
# This class is a Celluloid::IO actor which provides a bareboens HTTP server
# For HTTPS support, use Reel::SSLServer
# 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

# Allow the existing `new` to be called, even though we will
# replace it with a default version that creates HTTP servers over
# TCP sockets.
#
class << self
alias_method :_new, :new
protected :_new
end

# Create a new Reel HTTP server
#
# @param [String] host address to bind to
# @param [Fixnum] port to bind to
# @option options [Fixnum] backlog of requests to accept
# @option options [true] spy on the request
#
# @return [Reel::SSLServer] Reel HTTPS server actor
#
# ::new was overridden for backwards compatibility. The underlying
# #initialize method now accepts a `server` param that is
# responsible for having established the bi-directional
# communication channel. ::new uses the existing (sane) default of
# setting up the TCP channel for the user.
#
def self.new(host, port, options = {} , &callback)
server = Celluloid::IO::TCPServer.new(host, port)
backlog = options.fetch(:backlog, DEFAULT_BACKLOG)

# prevent TCP packets from being buffered
server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
server.listen(backlog)

self._new(server, options, &callback)
end

# Create a Reel HTTP server over a UNIX socket.
#
# @param [String] socket_path path to the UNIX socket
# @option options [true] spy on the request
#
def self.unix(socket_path, options = {}, &callback)
server = Celluloid::IO::UNIXServer.new(socket_path)

self._new(server, options, &callback)
end

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

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

async.run
end

def shutdown
@server.close if @server
Expand All @@ -75,6 +38,12 @@ 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'
Expand Down
20 changes: 20 additions & 0 deletions lib/reel/server/http.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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
Loading

0 comments on commit e28dad8

Please sign in to comment.