funnel 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/funnel CHANGED
@@ -12,7 +12,7 @@ OptionParser.new do |opts|
12
12
  opts.banner = "Usage: funnel [options] application_name"
13
13
 
14
14
  options[:verbose] = false
15
-
15
+
16
16
  opts.on( '-v', '--verbose', 'Output more information' ) do
17
17
  options[:verbose] = true
18
18
  end
@@ -21,24 +21,12 @@ OptionParser.new do |opts|
21
21
  puts opts
22
22
  exit
23
23
  end
24
-
25
- opts.on( '-w amqp', '--with-amqp', 'Include amqp proxy handler' ) do
26
- options[:amqp] = true
27
- end
28
-
29
- opts.on( '-w xmpp', '--with-xmpp', 'Include xmpp proxy handler' ) do
30
- options[:xmpp] = true
31
- end
32
-
33
- opts.on( '-w twitter', '--with-twitter', 'Include twitter stream proxy handler' ) do
34
- options[:twitter] = true
35
- end
36
-
24
+
37
25
  if ARGV.length < 1
38
26
  puts opts
39
27
  exit
40
28
  end
41
-
29
+
42
30
  end.parse!
43
31
 
44
32
  app = Funnel::Generators::Application.new
data/funnel.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'funnel'
3
- s.version = '0.1.5'
3
+ s.version = '0.1.6'
4
4
  s.date = '2009-12-30'
5
5
  s.summary = 'A realtime resource-centric application framework'
6
6
  s.email = 'dan.simpson@gmail.com'
@@ -14,12 +14,13 @@ spec = Gem::Specification.new do |s|
14
14
  s.authors = ['Dan Simpson']
15
15
  s.add_dependency('eventmachine', '>= 0.12.4')
16
16
 
17
-
18
17
  s.files = [
19
- 'README.markdown',
20
- 'funnel.gemspec',
21
- 'lib/funnel.rb',
18
+ 'README.markdown',
19
+ 'funnel.gemspec',
20
+ 'lib/funnel.rb',
22
21
  'lib/funnel/logger.rb',
22
+ 'lib/funnel/configuration.rb',
23
+ 'lib/funnel/flash/policy.rb',
23
24
  'lib/funnel/web_socket/frame.rb',
24
25
  'lib/funnel/web_socket/headers.rb',
25
26
  'lib/funnel/web_socket/connection.rb',
@@ -27,18 +28,11 @@ spec = Gem::Specification.new do |s|
27
28
  'lib/funnel/routing/routes.rb',
28
29
  'lib/funnel/routing/router.rb',
29
30
  'lib/funnel/server.rb',
30
- 'lib/funnel/servers/dummy_server.rb',
31
31
  'lib/funnel/generators/application.rb',
32
32
  'templates/application/config/boot.rb',
33
- 'templates/application/config/config.rb',
33
+ 'templates/application/config/settings.yml',
34
34
  'templates/application/config/routes.rb',
35
- 'templates/application/script/run',
36
- 'templates/application/handlers/echo_handler.rb',
37
- 'templates/amqp/config/amqp.rb',
38
- 'templates/amqp/handlers/amqp.rb',
39
- 'templates/xmpp/config/xmpp.rb',
40
- 'templates/xmpp/handlers/xmpp.rb',
41
- 'templates/twitter/config/twitter.rb',
42
- 'templates/twitter/handlers/twitter.rb'
35
+ 'templates/application/script/server',
36
+ 'templates/application/handlers/test_handler.rb',
43
37
  ]
44
38
  end
@@ -0,0 +1,32 @@
1
+ module Funnel
2
+
3
+ class Configuration
4
+
5
+ @@map = {
6
+ :host => "0.0.0.0",
7
+ :port => 4000,
8
+ :accepted_origins => []
9
+ }
10
+
11
+ def self.get key
12
+ @@map[key.to_sym]
13
+ end
14
+
15
+ def self.put key, val
16
+ @@map[key.to_sym] = val
17
+ end
18
+
19
+ def self.parse file
20
+ begin
21
+ settings = YAML.load_file(File.join(FUNNEL_ROOT, file))
22
+ settings.each do |key, val|
23
+ put key, val
24
+ end
25
+ rescue
26
+ raise IOError, "#{File.join(FUNNEL_ROOT, file)} could not be loaded."
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,29 @@
1
+ module Funnel
2
+ module Flash
3
+ class Policy
4
+
5
+ def self.response
6
+ settings = Funnel::Configuration
7
+ accepted = settings.get(:accepted_origins)
8
+
9
+ response = "<?xml version=\"1.0\"?>"
10
+ response << "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">"
11
+ response << "<cross-domain-policy>"
12
+
13
+ # use the configuration settings to determine who
14
+ # can connect via the flash socket
15
+ if accepted.empty?
16
+ response << "<allow-access-from domain=\"*\" to-ports=\"#{settings.get(:port)}\"/>")
17
+ else
18
+ accepted.each do |domain|
19
+ response << "<allow-access-from domain=\"#{domain}\" to-ports=\"#{settings.get(:port)}\"/>")
20
+ end
21
+ end
22
+
23
+ response << "</cross-domain-policy>"
24
+ response
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -3,26 +3,13 @@ require 'fileutils'
3
3
  module Funnel
4
4
  module Generators
5
5
  class Application
6
-
7
-
6
+
8
7
  def generate destination, options={}
9
8
  FileUtils.cp_r(template_dir, destination)
10
-
11
- if options[:amqp]
12
- FileUtils.cp_r(template_dir("amqp"), destination)
13
- end
14
-
15
- if options[:xmpp]
16
- FileUtils.cp_r(template_dir("xmpp"), destination)
17
- end
18
-
19
- if options[:twitter]
20
- FileUtils.cp_r(template_dir("twitter"), destination)
21
- end
22
9
  end
23
-
10
+
24
11
  private
25
-
12
+
26
13
  def template_dir tpl="application"
27
14
  File.expand_path("../../../../templates/#{tpl}", __FILE__)
28
15
  end
data/lib/funnel/logger.rb CHANGED
@@ -17,7 +17,7 @@ class Logger
17
17
  private
18
18
 
19
19
  def self.log msg
20
- puts "#{Time.now} - #{msg}"
20
+ p "#{Time.now} - #{msg}"
21
21
  end
22
22
 
23
23
 
@@ -1,19 +1,16 @@
1
1
  module Funnel
2
2
  module Routing
3
-
4
3
  class Route
5
-
6
4
 
7
5
  def initialize path, opts
8
6
  @opts = opts || {}
9
7
  @opts[:path] = path
10
8
  end
11
-
9
+
12
10
  def handler
13
11
  @opts[:handler]
14
12
  end
15
-
16
-
13
+
17
14
  end
18
15
  end
19
16
  end
@@ -1,28 +1,40 @@
1
1
  module Funnel
2
2
  module Routing
3
-
3
+
4
4
  class Router < WebSocket::Connection
5
-
5
+
6
6
  # parse headers, validate headers, accept
7
7
  # connection, and change the default handler
8
8
  def receive_data data
9
-
9
+
10
+ if is_flash_policy_request?(data)
11
+ send_data Funnel::Flash::Policy.response
12
+ close_connection and return
13
+ end
14
+
10
15
  @headers = WebSocket::Headers.parse(data)
11
-
12
- if valid_connection? && valid_route?
16
+
17
+ if valid_connection? && valid_origin?
13
18
  accept_connection
14
19
  defer_connection
15
20
  else
16
21
  close_connection
17
22
  end
18
-
23
+
19
24
  end
20
-
25
+
21
26
  # is this a websocket connection?
22
27
  def valid_connection?
23
28
  @headers[:connection] =~ /upgrade/i && @headers[:upgrade] =~ /websocket/i
24
29
  end
25
30
 
31
+ # we can limit the availability to this service
32
+ # by setting the accepted_origins configuration
33
+ def valid_origin?
34
+ accepted = Funnel::Configuration.get(:accepted_origins)
35
+ accepted.empty? or accepted.include?(@headers[:origin])
36
+ end
37
+
26
38
  # TODO: add WebSocket-Protocol
27
39
  def accept_connection
28
40
  response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
@@ -30,25 +42,31 @@ module Funnel
30
42
  response << "Connection: Upgrade\r\n"
31
43
  response << "WebSocket-Origin: #{origin}\r\n"
32
44
  response << "WebSocket-Location: ws://#{host}#{path}\r\n\r\n"
33
-
45
+
34
46
  send_data response
35
47
  end
36
-
48
+
37
49
  # change the handler from this instance, to a new instance
38
50
  # of the type defined by the routing map
39
51
  def defer_connection
40
- puts "got this far"
41
- EM.instance_variable_get("@conns")[@signature] = Routes.get_handler(path).send(:new, @signature)
42
- end
43
52
 
44
- def valid_route?
45
- true
53
+ conn = Routes.get_handler(path).send(:new, @signature)
54
+ conn.headers = @headers
55
+
56
+ EM.instance_variable_get("@conns")[@signature] = conn
46
57
  end
47
58
 
48
59
  def requested_protocol
49
60
  @header[:websocket_protocol] || :default
50
61
  end
51
-
62
+
63
+ # since we expect some client to be connecting
64
+ # via a flash socket, we need to handle the policy
65
+ # request if we detect '<' char
66
+ def is_flash_policy_request? data
67
+ data[0] == ?<
68
+ end
69
+
52
70
  end
53
71
  end
54
72
  end
@@ -28,13 +28,22 @@ module Funnel
28
28
  end
29
29
 
30
30
  def get_default
31
- @routes.key?(:default) ? @routes[:default].handler : Funnel::Servers::DummyServer
31
+ raise_no_default_error unless @routes.key?(:default)
32
+
33
+ @routes[:default].handler
32
34
  end
33
35
 
34
36
  def get_handler path
35
37
  @routes.key?(path) ? @routes[path].handler : @routes[:default].handler
36
38
  end
37
39
 
40
+ def raise_no_default_error
41
+ str = "No default route detected. Please supply a"
42
+ str << " default route in your config/routes.rb file. "
43
+ str << " Eg: map.default :handler => TestHandler"
44
+ raise str
45
+ end
46
+
38
47
  end
39
48
  end
40
49
  end
data/lib/funnel/server.rb CHANGED
@@ -1,25 +1,26 @@
1
1
  module Funnel
2
2
  class Server
3
-
3
+
4
4
  def self.start host, port
5
-
5
+
6
+ raise "Reactor already running" if EM.reactor_running?
7
+
6
8
  EM.epoll if EM.epoll?
7
9
  EM.kqueue if EM.kqueue?
8
-
10
+
9
11
  EM.run do
10
-
12
+
11
13
  trap("TERM") { stop }
12
14
  trap("INT") { stop }
13
-
15
+
14
16
  EM.start_server(host, port, Routing::Router)
15
-
17
+
16
18
  end
17
-
18
19
  end
19
-
20
+
20
21
  def self.stop
21
22
  EM.stop
22
23
  end
23
-
24
+
24
25
  end
25
26
  end
File without changes
File without changes
data/lib/funnel.rb CHANGED
@@ -7,7 +7,9 @@ module Funnel
7
7
  end
8
8
  end
9
9
 
10
+ require 'funnel/configuration.rb'
10
11
  require 'funnel/logger.rb'
12
+ require 'funnel/flash/policy.rb'
11
13
  require 'funnel/generators/application.rb'
12
14
  require 'funnel/web_socket/frame.rb'
13
15
  require 'funnel/web_socket/headers.rb'
@@ -15,5 +17,4 @@ require 'funnel/web_socket/connection.rb'
15
17
  require 'funnel/routing/route.rb'
16
18
  require 'funnel/routing/routes.rb'
17
19
  require 'funnel/routing/router.rb'
18
- require 'funnel/server.rb'
19
- require 'funnel/servers/dummy_server.rb'
20
+ require 'funnel/server.rb'
@@ -1,24 +1,23 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
- FUNNEL_ROOT = File.expand_path('../../', __FILE__)
2
+ FUNNEL_ROOT = File.expand_path("../../", __FILE__)
3
3
 
4
- require 'rubygems'
5
- require 'funnel'
4
+ require "rubygems"
5
+ require "funnel"
6
6
 
7
- #local includes
8
- require 'config'
9
- require 'funnel'
7
+ Funnel::Configuration.load("config/settings.yaml")
10
8
 
11
- #load servers defined by user
9
+ #load handlers defined by user
12
10
  handlers = Dir.entries(FUNNEL_ROOT + "/handlers").select { |n| not n =~ /^\.+$/ }
13
11
  if handlers
14
12
  handlers.each do |h|
15
- puts h
16
13
  require FUNNEL_ROOT + "/handlers/#{h}"
17
14
  end
18
15
  end
19
16
 
20
- #require routes and map them
21
- require 'routes'
17
+ require "routes"
22
18
 
23
19
  #start the server
24
- Funnel::Server.start(Configuration.get(:host), Configuration.get(:port))
20
+ Funnel::Server.start(
21
+ Funnel::Configuration.get(:host),
22
+ Funnel::Configuration.get(:port)
23
+ )
@@ -0,0 +1,5 @@
1
+ host: 0.0.0.0
2
+ port: 4000
3
+ accepted_origins:
4
+ - localhost
5
+ - test.com
@@ -0,0 +1,21 @@
1
+ class TestHandler < Funnel::WebSocket::Connection
2
+
3
+ #called when the connection is ready to send
4
+ #and receive data
5
+ def on_ready
6
+ puts "A connection has been established"
7
+ end
8
+
9
+ #called on disconnect
10
+ def on_disconnect
11
+ puts "A connection has been closed"
12
+ end
13
+
14
+ #right back at you
15
+ def on_data msg
16
+ puts "A message was received #{msg}"
17
+ send_message msg
18
+ puts "A message was sent #{msg}"
19
+ end
20
+
21
+ end
File without changes
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: funnel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 6
9
+ version: 0.1.6
5
10
  platform: ruby
6
11
  authors:
7
12
  - Dan Simpson
@@ -14,14 +19,18 @@ default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: eventmachine
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 12
30
+ - 4
23
31
  version: 0.12.4
24
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  description: A realtime resource-centric application framework
26
35
  email: dan.simpson@gmail.com
27
36
  executables:
@@ -35,6 +44,8 @@ files:
35
44
  - funnel.gemspec
36
45
  - lib/funnel.rb
37
46
  - lib/funnel/logger.rb
47
+ - lib/funnel/configuration.rb
48
+ - lib/funnel/flash/policy.rb
38
49
  - lib/funnel/web_socket/frame.rb
39
50
  - lib/funnel/web_socket/headers.rb
40
51
  - lib/funnel/web_socket/connection.rb
@@ -42,19 +53,12 @@ files:
42
53
  - lib/funnel/routing/routes.rb
43
54
  - lib/funnel/routing/router.rb
44
55
  - lib/funnel/server.rb
45
- - lib/funnel/servers/dummy_server.rb
46
56
  - lib/funnel/generators/application.rb
47
57
  - templates/application/config/boot.rb
48
- - templates/application/config/config.rb
58
+ - templates/application/config/settings.yml
49
59
  - templates/application/config/routes.rb
50
- - templates/application/script/run
51
- - templates/application/handlers/echo_handler.rb
52
- - templates/amqp/config/amqp.rb
53
- - templates/amqp/handlers/amqp.rb
54
- - templates/xmpp/config/xmpp.rb
55
- - templates/xmpp/handlers/xmpp.rb
56
- - templates/twitter/config/twitter.rb
57
- - templates/twitter/handlers/twitter.rb
60
+ - templates/application/script/server
61
+ - templates/application/handlers/test_handler.rb
58
62
  has_rdoc: true
59
63
  homepage: http://github.com/dansimpson/funnel
60
64
  licenses: []
@@ -68,18 +72,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
72
  requirements:
69
73
  - - ">="
70
74
  - !ruby/object:Gem::Version
75
+ segments:
76
+ - 0
71
77
  version: "0"
72
- version:
73
78
  required_rubygems_version: !ruby/object:Gem::Requirement
74
79
  requirements:
75
80
  - - ">="
76
81
  - !ruby/object:Gem::Version
82
+ segments:
83
+ - 0
77
84
  version: "0"
78
- version:
79
85
  requirements: []
80
86
 
81
87
  rubyforge_project:
82
- rubygems_version: 1.3.5
88
+ rubygems_version: 1.3.6
83
89
  signing_key:
84
90
  specification_version: 3
85
91
  summary: A realtime resource-centric application framework
@@ -1,20 +0,0 @@
1
- module Funnel
2
- module Servers
3
- class DummyServer < Funnel::WebSocket::Connection
4
-
5
- def on_ready
6
- Logger.info("DummyServer - on_ready")
7
- end
8
-
9
- def on_data data
10
- Logger.info("DummyServer - on_data")
11
- end
12
-
13
- def on_disconnect
14
- Logger.info("DummyServer - on_disconnect")
15
- end
16
-
17
- end
18
- end
19
- end
20
-
File without changes
File without changes
@@ -1,16 +0,0 @@
1
- class Configuration
2
-
3
- @@map = {
4
- :host => "0.0.0.0",
5
- :port => 4000
6
- }
7
-
8
- def self.get key
9
- @@map[key.to_sym]
10
- end
11
-
12
- def self.put key, val
13
- @@map[key.to_sym] = val
14
- end
15
-
16
- end
@@ -1,19 +0,0 @@
1
- class EchoHandler < Funnel::WebSocket::Connection
2
-
3
- #called when the connection is ready to send
4
- #and receive data
5
- def on_ready
6
- puts "Ready..."
7
- end
8
-
9
- #called on disconnect
10
- def on_disconnect
11
- puts "Ended"
12
- end
13
-
14
- #right back at you
15
- def on_data msg
16
- send_message msg
17
- end
18
-
19
- end
File without changes
File without changes
File without changes
File without changes