funnel 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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