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 +3 -15
- data/funnel.gemspec +9 -15
- data/lib/funnel/configuration.rb +32 -0
- data/lib/funnel/flash/policy.rb +29 -0
- data/lib/funnel/generators/application.rb +3 -16
- data/lib/funnel/logger.rb +1 -1
- data/lib/funnel/routing/route.rb +2 -5
- data/lib/funnel/routing/router.rb +33 -15
- data/lib/funnel/routing/routes.rb +10 -1
- data/lib/funnel/server.rb +10 -9
- data/lib/funnel/web_socket/connection.rb +0 -0
- data/lib/funnel/web_socket/frame.rb +0 -0
- data/lib/funnel.rb +3 -2
- data/templates/application/config/boot.rb +10 -11
- data/templates/application/config/settings.yml +5 -0
- data/templates/application/handlers/test_handler.rb +21 -0
- data/templates/application/script/{run → server} +0 -0
- metadata +24 -18
- data/lib/funnel/servers/dummy_server.rb +0 -20
- data/templates/amqp/config/amqp.rb +0 -0
- data/templates/amqp/handlers/amqp.rb +0 -0
- data/templates/application/config/config.rb +0 -16
- data/templates/application/handlers/echo_handler.rb +0 -19
- data/templates/twitter/config/twitter.rb +0 -0
- data/templates/twitter/handlers/twitter.rb +0 -0
- data/templates/xmpp/config/xmpp.rb +0 -0
- data/templates/xmpp/handlers/xmpp.rb +0 -0
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.
|
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
|
-
|
20
|
-
|
21
|
-
|
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/
|
33
|
+
'templates/application/config/settings.yml',
|
34
34
|
'templates/application/config/routes.rb',
|
35
|
-
'templates/application/script/
|
36
|
-
'templates/application/handlers/
|
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
data/lib/funnel/routing/route.rb
CHANGED
@@ -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? &&
|
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
|
-
|
45
|
-
|
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)
|
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(
|
2
|
+
FUNNEL_ROOT = File.expand_path("../../", __FILE__)
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require "rubygems"
|
5
|
+
require "funnel"
|
6
6
|
|
7
|
-
|
8
|
-
require 'config'
|
9
|
-
require 'funnel'
|
7
|
+
Funnel::Configuration.load("config/settings.yaml")
|
10
8
|
|
11
|
-
#load
|
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
|
-
|
21
|
-
require 'routes'
|
17
|
+
require "routes"
|
22
18
|
|
23
19
|
#start the server
|
24
|
-
Funnel::Server.start(
|
20
|
+
Funnel::Server.start(
|
21
|
+
Funnel::Configuration.get(:host),
|
22
|
+
Funnel::Configuration.get(:port)
|
23
|
+
)
|
@@ -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
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
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/
|
58
|
+
- templates/application/config/settings.yml
|
49
59
|
- templates/application/config/routes.rb
|
50
|
-
- templates/application/script/
|
51
|
-
- templates/application/handlers/
|
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.
|
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,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
|