reel-0.6.1/ 0000755 0000041 0000041 00000000000 12676060250 012501 5 ustar www-data www-data reel-0.6.1/Rakefile 0000644 0000041 0000041 00000000230 12676060250 014141 0 ustar www-data www-data #!/usr/bin/env rake require "bundler/gem_tasks" Dir[File.expand_path("../tasks/**/*.rake", __FILE__)].each { |task| load task } task :default => :spec reel-0.6.1/Gemfile 0000644 0000041 0000041 00000000551 12676060250 013775 0 ustar www-data www-data source 'https://rubygems.org' gem 'celluloid' gem 'celluloid-io' gem 'http' gem 'jruby-openssl' if defined? JRUBY_VERSION gem 'coveralls', require: false # Specify your gem's dependencies in reel.gemspec gemspec group :development do gem 'guard-rspec' gem 'pry' end platforms :rbx do gem 'racc' gem 'rubinius-coverage' gem 'rubysl', '~> 2.0' end reel-0.6.1/examples/ 0000755 0000041 0000041 00000000000 12676060250 014317 5 ustar www-data www-data reel-0.6.1/examples/server_sent_events.rb 0000755 0000041 0000041 00000007111 12676060250 020572 0 ustar www-data www-data #!/usr/bin/env ruby # See: http://www.w3.org/TR/eventsource/ # Run with: bundle exec examples/server_sent_events.rb # Test with: curl -vNH 'Accept: text/event-stream' -H 'Last-Event-ID: 1' -H 'Cache-Control: no-cache' http://localhost:63310 require 'bundler/setup' require 'time' require 'reel' class ServerSentEvents < Reel::Server::HTTP include Celluloid::Logger def initialize(ip = '127.0.0.1', port = 63310) @connections = [] @history = [] @lastEventId = 0 async.ping async.ring #not needed for Production, only to have some events here. super(ip, port, &method(:on_connection)) end #broadcasts events to all clients def broadcast(event, data) #only keep the last 5000 Events if @history.size >= 6000 @history.slice!(0, @history.size - 1000) end @lastEventId += 1 @history << {id: @lastEventId, event: event, data: data} info "Sending Event: #{event} Data: #{data} to #{@connections.count} Clients" @connections.each do |socket| async.send_sse(socket, data, event, @lastEventId) end true end private #event and id are optional, Eventsource only needs data def send_sse(socket, data, event = nil, id = nil) begin socket.id id if id socket.event event if event socket.data data rescue Reel::SocketError, NoMethodError @connections.delete(socket) if @connections.include?(socket) end end #Lines that start with a Colon are Comments and will be ignored def send_ping @connections.each do |socket| begin socket << ":\n" rescue Reel::SocketError @connections.delete(socket) end end end #apache 2.2 closes connections after five seconds when nothing is send, see this as a poor mans Keep-Alive def ping every(5) do send_ping end end #only used to have some events here, not needed for Production. def ring every(2) do broadcast(:time, Time.now.httpdate) end end def handle_request(request) query = {} (request.query_string || '').split('&').each do |kv| key, value = kv.split('=') if key && value key, value = CGI.unescape(key), CGI.unescape(value) query[key] = value end end #see https://github.com/celluloid/reel/blob/master/lib/reel/stream.rb#L35 eventStream = Reel::EventStream.new do |socket| @connections << socket socket.retry 5000 #after a Connection reset resend newer Messages to the Client, query['lastEventId'] is needed for https://github.com/Yaffle/EventSource if @history.count > 0 && id = (request.headers['Last-Event-ID'] || query['lastEventId']) begin if history = @history.select {|h| h[:id] >= Integer(id)}.map {|a| "id: %d\nevent: %s\ndata: %s" % [a[:id], a[:event], a[:data]]}.join("\n\n") socket << "%s\n\n" % [history] else socket << "id\n\n" end rescue ArgumentError, Reel::SocketError @connections.delete(socket) request.close end else socket << "id\n\n" end end #X-Accel-Buffering is nginx(?) specific. Setting this to "no" will allow unbuffered responses suitable for Comet and HTTP streaming applications request.respond Reel::StreamResponse.new(:ok, { 'Content-Type' => 'text/event-stream; charset=utf-8', 'Cache-Control' => 'no-cache', 'X-Accel-Buffering' => 'no', 'Access-Control-Allow-Origin' => '*'}, eventStream) end def on_connection(connection) connection.each_request do |request| handle_request(request) end end end ServerSentEvents.run reel-0.6.1/examples/spy_hello_world.rb 0000755 0000041 0000041 00000001246 12676060250 020057 0 ustar www-data www-data #!/usr/bin/env ruby # Run with: bundle exec examples/hello_world.rb require 'rubygems' require 'bundler/setup' require 'reel' addr, port = '127.0.0.1', 1234 puts "*** Starting server on http://#{addr}:#{port}" 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. # route request.url request.respond :ok, "hello, world!\n" end # Reel takes care of closing the connection for you # If you would like to hand the connection off to another thread or actor, # use, connection.detach and then manually call connection.close when done end reel-0.6.1/examples/hello_world.rb 0000755 0000041 0000041 00000001231 12676060250 017156 0 ustar www-data www-data #!/usr/bin/env ruby # Run with: bundle exec examples/hello_world.rb require 'rubygems' require 'bundler/setup' require 'reel' addr, port = '127.0.0.1', 1234 puts "*** Starting server on http://#{addr}:#{port}" 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. # route request.url request.respond :ok, "hello, world!" end # Reel takes care of closing the connection for you # If you would like to hand the connection off to another thread or actor, # use, connection.detach and then manually call connection.close when done end reel-0.6.1/examples/https_hello_world.rb 0000755 0000041 0000041 00000001536 12676060250 020410 0 ustar www-data www-data #!/usr/bin/env ruby # Run with: bundle exec examples/hello_world.rb require 'rubygems' require 'bundler/setup' require 'reel' addr, port = '127.0.0.1', 4430 options = { :cert => File.read(File.expand_path("../../spec/fixtures/server.crt", __FILE__)), :key => File.read(File.expand_path("../../spec/fixtures/server.key", __FILE__)) } puts "*** Starting server on #{addr}:#{port}" Reel::Server::HTTPS.supervise(addr, port, options) do |connection| # For keep-alive support connection.each_request do |request| # Ordinarily we'd route the request here, e.g. # route request.url request.respond :ok, "hello, world!" end # Reel takes care of closing the connection for you # If you would like to hand the connection off to another thread or actor, # use, connection.detach and then manually call connection.close when done end sleep reel-0.6.1/examples/websockets.rb 0000755 0000041 0000041 00000006514 12676060250 017026 0 ustar www-data www-data require 'rubygems' require 'bundler/setup' require 'reel' require 'celluloid/autostart' class TimeServer include Celluloid include Celluloid::Notifications def initialize async.run end def run now = Time.now.to_f sleep now.ceil - now + 0.001 every(1) { publish 'time_change', Time.now } end end class TimeClient include Celluloid include Celluloid::Notifications include Celluloid::Logger def initialize(websocket) info "Streaming time changes to client" @socket = websocket subscribe('time_change', :notify_time_change) end def notify_time_change(topic, new_time) @socket << new_time.inspect rescue Reel::SocketError info "Time client disconnected" terminate end end class WebServer < Reel::Server::HTTP include Celluloid::Logger def initialize(host = "127.0.0.1", port = 1234) info "Time server example starting on #{host}:#{port}" super(host, port, &method(:on_connection)) end def on_connection(connection) while request = connection.request if request.websocket? info "Received a WebSocket connection" # We're going to hand off this connection to another actor (TimeClient) # However, initially Reel::Connections are "attached" to the # 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 (in this case, Reel::Server::HTTP) connection.detach route_websocket request.websocket return else route_request connection, request end end end def route_request(connection, request) if request.url == "/" return render_index(connection) end info "404 Not Found: #{request.path}" connection.respond :not_found, "Not found" end def route_websocket(socket) if socket.url == "/timeinfo" TimeClient.new(socket) else info "Received invalid WebSocket request for: #{socket.url}" socket.close end end def render_index(connection) info "200 OK: /" connection.respond :ok, <<-HTML