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 +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
|