skates 0.1.11 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -1
- data/lib/skates/client_connection.rb +31 -32
- data/lib/skates/component_connection.rb +27 -0
- data/lib/skates/generator.rb +3 -0
- data/lib/skates/xmpp_connection.rb +47 -10
- data/spec/lib/babylon/client_connection_spec.rb +35 -30
- data/spec/lib/babylon/component_connection_spec.rb +10 -1
- data/spec/lib/babylon/xmpp_connection_spec.rb +30 -5
- data/templates/skates/config/boot.rb +3 -1
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -85,7 +85,7 @@ Skates's edge versions are located at Github : http://github.com/julien51/skates
|
|
85
85
|
|
86
86
|
== REQUIREMENTS :
|
87
87
|
|
88
|
-
Gems : Eventmachine, nokogiri
|
88
|
+
Gems : Eventmachine, nokogiri, YAML, log4r, sax-machine, templater
|
89
89
|
|
90
90
|
== LICENSE:
|
91
91
|
|
@@ -16,41 +16,40 @@ module Skates
|
|
16
16
|
end
|
17
17
|
|
18
18
|
##
|
19
|
-
# Connects the ClientConnection based on SRV records for the jid's domain, if no host
|
20
|
-
#
|
19
|
+
# Connects the ClientConnection based on SRV records for the jid's domain, if no host has been provided.
|
20
|
+
# It will not resolve if params["host"] is an IP.
|
21
|
+
# And it will always use
|
21
22
|
def self.connect(params, handler = nil)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
}
|
33
|
-
srv = dns.getresources("_xmpp-client._tcp.#{host_from_jid}", Resolv::DNS::Resource::IN::SRV)
|
34
|
-
}
|
35
|
-
# Sort SRV records: lowest priority first, highest weight first
|
36
|
-
srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) }
|
37
|
-
# And now, for each record, let's try to connect.
|
38
|
-
srv.each { |record|
|
39
|
-
begin
|
40
|
-
params["host"] = record.target.to_s
|
41
|
-
params["port"] = Integer(record.port)
|
42
|
-
super(params, handler)
|
43
|
-
# Success
|
44
|
-
break
|
45
|
-
rescue NotConnected
|
46
|
-
# Try next SRV record
|
47
|
-
end
|
48
|
-
}
|
49
|
-
rescue NameError
|
23
|
+
params["host"] ||= params["jid"].split("/").first.split("@").last
|
24
|
+
super(params, handler)
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Resolution for clients, based on SRV records
|
29
|
+
def self.resolve(host, &block)
|
30
|
+
Resolv::DNS.open { |dns|
|
31
|
+
# If ruby version is too old and SRV is unknown, this will raise a NameError
|
32
|
+
# which is caught below
|
50
33
|
Skates.logger.debug {
|
51
|
-
"
|
34
|
+
"RESOLVING: #{srv_for_host(host)} (SRV)"
|
52
35
|
}
|
53
|
-
|
36
|
+
begin
|
37
|
+
srv = dns.getresources("_xmpp-client._tcp.#{host}", Resolv::DNS::Resource::IN::SRV)
|
38
|
+
# Sort SRV records: lowest priority first, highest weight first
|
39
|
+
srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) }
|
40
|
+
# And now, for each record, let's try to connect.
|
41
|
+
srv.each { |record|
|
42
|
+
ip = record.target.to_s
|
43
|
+
port = Integer(record.port)
|
44
|
+
break if block.call({"host" => ip, "port" => port})
|
45
|
+
}
|
46
|
+
block.call(false) # bleh, we couldn't resolve to any valid. Too bad.
|
47
|
+
rescue NameError
|
48
|
+
Skates.logger.debug {
|
49
|
+
"Resolv::DNS does not support SRV records. Please upgrade to ruby-1.8.3 or later! \n#{$!} : #{$!.backtrace.join("\n")}"
|
50
|
+
}
|
51
|
+
end
|
52
|
+
}
|
54
53
|
end
|
55
54
|
|
56
55
|
##
|
@@ -4,6 +4,11 @@ module Skates
|
|
4
4
|
# Upon stanza reception, and depending on the status (connected... etc), this component will handle or forward the stanzas.
|
5
5
|
class ComponentConnection < XmppConnection
|
6
6
|
|
7
|
+
def self.connect(params, handler)
|
8
|
+
params["host"] ||= params["jid"]
|
9
|
+
super(params, handler)
|
10
|
+
end
|
11
|
+
|
7
12
|
##
|
8
13
|
# Creates a new ComponentConnection and waits for data in the stream
|
9
14
|
def initialize(params)
|
@@ -72,6 +77,28 @@ module Skates
|
|
72
77
|
'jabber:component:accept'
|
73
78
|
end
|
74
79
|
|
80
|
+
##
|
81
|
+
# Resolution for Components, based on SRV records
|
82
|
+
def self.resolve(host, &block)
|
83
|
+
Resolv::DNS.open { |dns|
|
84
|
+
# If ruby version is too old and SRV is unknown, this will raise a NameError
|
85
|
+
# which is caught below
|
86
|
+
Skates.logger.debug {
|
87
|
+
"RESOLVING: #{host} "
|
88
|
+
}
|
89
|
+
found = false
|
90
|
+
records = dns.getresources(host, Resolv::DNS::Resource::IN::A)
|
91
|
+
records.each do |record|
|
92
|
+
ip = record.address.to_s
|
93
|
+
if block.call({"host" => ip})
|
94
|
+
found = true
|
95
|
+
break
|
96
|
+
end
|
97
|
+
end
|
98
|
+
block.call(false) unless found # bleh, we couldn't resolve to any valid. Too bad.
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
75
102
|
private
|
76
103
|
|
77
104
|
def handshake(stanza)
|
data/lib/skates/generator.rb
CHANGED
@@ -38,6 +38,9 @@ module Skates
|
|
38
38
|
empty_directory :initializers_directory do |d|
|
39
39
|
d.destination = "#{application_name}/config/initializers"
|
40
40
|
end
|
41
|
+
empty_directory :destructors_directory do |d|
|
42
|
+
d.destination = "#{application_name}/config/destructors"
|
43
|
+
end
|
41
44
|
empty_directory :tmp_directory do |d|
|
42
45
|
d.destination = "#{application_name}/tmp"
|
43
46
|
end
|
@@ -28,6 +28,13 @@ module Skates
|
|
28
28
|
|
29
29
|
@@max_stanza_size = 65535
|
30
30
|
|
31
|
+
##
|
32
|
+
# This will the host asynscrhonously and calls the block for each IP:Port pair.
|
33
|
+
# if the block returns true, no other record will be tried. If it returns false, the block will be called with the next pair.
|
34
|
+
def self.resolve(host, &block)
|
35
|
+
block.call(false)
|
36
|
+
end
|
37
|
+
|
31
38
|
##
|
32
39
|
# Maximum Stanza size. Default is 65535
|
33
40
|
def self.max_stanza_size
|
@@ -45,16 +52,26 @@ module Skates
|
|
45
52
|
# It passes itself (as handler) and the configuration
|
46
53
|
# This can very well be overwritten by subclasses.
|
47
54
|
def self.connect(params, handler)
|
48
|
-
|
49
|
-
"
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
if params["host"] =~ /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/
|
56
|
+
params["port"] = params["port"] ? params["port"].to_i : 5222
|
57
|
+
_connect(params, handler)
|
58
|
+
else
|
59
|
+
resolve(params["host"]) do |host_info|
|
60
|
+
if host_info
|
61
|
+
begin
|
62
|
+
_connect(params.merge(host_info), handler)
|
63
|
+
true # connected! Yay!
|
64
|
+
rescue NotConnected
|
65
|
+
# It will try the next pair of ip/port
|
66
|
+
false
|
67
|
+
end
|
68
|
+
else
|
69
|
+
Skates.logger.error {
|
70
|
+
"Sorry, we couldn't resolve #{srv_for_host(params["host"])} to any host that accept XMPP connections. Please provide a params[\"host\"]."
|
71
|
+
}
|
72
|
+
EM.stop_event_loop
|
73
|
+
end
|
74
|
+
end
|
58
75
|
end
|
59
76
|
end
|
60
77
|
|
@@ -167,6 +184,26 @@ module Skates
|
|
167
184
|
}
|
168
185
|
end
|
169
186
|
end
|
187
|
+
|
188
|
+
def self.srv_for_host(host)
|
189
|
+
"#{host}"
|
190
|
+
end
|
191
|
+
|
192
|
+
def self._connect(params, handler)
|
193
|
+
Skates.logger.debug {
|
194
|
+
"CONNECTING TO #{params["host"]}:#{params["port"]} with #{handler.inspect} as connection handler" # Very low level Logging
|
195
|
+
}
|
196
|
+
begin
|
197
|
+
EventMachine.connect(params["host"], params["port"], self, params.merge({"handler" => handler}))
|
198
|
+
rescue RuntimeError
|
199
|
+
Skates.logger.error {
|
200
|
+
"CONNECTION ERROR : #{$!.class} => #{$!}" # Very low level Logging
|
201
|
+
}
|
202
|
+
raise NotConnected
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
170
207
|
end
|
171
208
|
|
172
209
|
end
|
@@ -6,8 +6,8 @@ describe Skates::ClientConnection do
|
|
6
6
|
include SkatesSpecHelper
|
7
7
|
|
8
8
|
before(:each) do
|
9
|
-
@params = {"jid" => "jid@server.tld", "password" => "password"
|
10
|
-
@client = Skates::ClientConnection.connect(@params, handler_mock)
|
9
|
+
@params = {"jid" => "jid@server.tld", "password" => "password"}
|
10
|
+
@client = Skates::ClientConnection.connect(@params.merge({"host" => "0.0.0.0", "port" => 5222}), handler_mock)
|
11
11
|
@client.stub!(:send_xml).and_return(true)
|
12
12
|
end
|
13
13
|
|
@@ -18,37 +18,42 @@ describe Skates::ClientConnection do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
describe "connect" do
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "when resolving the hostname" do
|
24
|
+
before(:each) do
|
25
|
+
@params.delete("host")
|
26
|
+
@params.delete("port")
|
27
|
+
@srv = [
|
28
|
+
mock(Resolv::DNS::Resource, :priority => 10, :target => "12.13.14.15", :port => 1234),
|
29
|
+
mock(Resolv::DNS::Resource, :priority => 3, :target => "12.13.14.16", :port => 4567),
|
30
|
+
mock(Resolv::DNS::Resource, :priority => 100, :target => "12.13.14.17", :port => 8910)
|
31
|
+
]
|
32
|
+
@mock_dns = mock(Object)
|
33
|
+
Resolv::DNS.stub!(:open).and_yield(@mock_dns)
|
34
|
+
@mock_dns.stub!(:getresources).and_return(@srv)
|
24
35
|
end
|
25
36
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
it "should sort the srv records" do
|
47
|
-
@client = Skates::ClientConnection.connect(@params, handler_mock)
|
48
|
-
@srv.map {|srv| srv.target }.should == ["xmpp2.server.tld", "xmpp.server.tld", "xmpp3.server.tld"]
|
37
|
+
it "should get resources assiated with _xmpp-client._tcp.host.tld}" do
|
38
|
+
Resolv::DNS.should_receive(:open).and_yield(@mock_dns)
|
39
|
+
@mock_dns.should_receive(:getresources).with("_xmpp-client._tcp.server.tld", Resolv::DNS::Resource::IN::SRV).and_return(@srv)
|
40
|
+
Skates::ClientConnection.resolve("server.tld")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should call the block with the highest priority" do
|
44
|
+
Skates::ClientConnection.resolve("xmpp.server.tld") do |params|
|
45
|
+
params["host"].should == "12.13.14.16"
|
46
|
+
params["port"].should == 4567
|
47
|
+
true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should call the block as many times as needed if they're not connecting" do
|
52
|
+
conn = mock(Skates::ClientConnection, :_connect => false)
|
53
|
+
conn.should_receive(:_connect).exactly(3).times
|
54
|
+
Skates::ClientConnection.resolve("xmpp.server.tld") do |ip, port|
|
55
|
+
conn._connect(ip, port)
|
49
56
|
end
|
50
|
-
|
51
|
-
it "should try to connect to each record until one of the them actually connects"
|
52
57
|
end
|
53
58
|
end
|
54
59
|
|
@@ -6,7 +6,7 @@ describe Skates::ComponentConnection do
|
|
6
6
|
include SkatesSpecHelper
|
7
7
|
|
8
8
|
before(:each) do
|
9
|
-
@params = {"jid" => "jid@server", "password" => "password", "port" => 1234, "host" => "
|
9
|
+
@params = {"jid" => "jid@server", "password" => "password", "port" => 1234, "host" => "0.0.0.0"}
|
10
10
|
@component = Skates::ComponentConnection.connect(@params, handler_mock)
|
11
11
|
@component.stub!(:send_xml).and_return(true)
|
12
12
|
end
|
@@ -132,4 +132,13 @@ describe Skates::ComponentConnection do
|
|
132
132
|
|
133
133
|
end
|
134
134
|
|
135
|
+
describe "when resolving" do
|
136
|
+
it "should resolve records" do
|
137
|
+
Skates::ComponentConnection.resolve("xmpp2.superfeedr.com") do |res|
|
138
|
+
res["host"].should == "173.45.226.99"
|
139
|
+
true
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
135
144
|
end
|
@@ -7,24 +7,49 @@ describe Skates::XmppConnection do
|
|
7
7
|
|
8
8
|
before(:each) do
|
9
9
|
@params = {"jid" => "jid@server", "password" => "password", "port" => 1234, "host" => "myhost.com"}
|
10
|
-
@connection = Skates::XmppConnection.
|
10
|
+
@connection = Skates::XmppConnection._connect(@params, handler_mock)
|
11
11
|
end
|
12
12
|
|
13
|
-
describe "
|
13
|
+
describe "_connect" do
|
14
14
|
it "should connect EventMachine and return it" do
|
15
15
|
EventMachine.should_receive(:connect).with(@params["host"], @params["port"], Skates::XmppConnection, hash_including("handler" => handler_mock)).and_return(@connection)
|
16
|
-
Skates::XmppConnection.
|
16
|
+
Skates::XmppConnection._connect(@params, handler_mock).should == @connection
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should rescue Connection Errors" do
|
20
20
|
EventMachine.stub!(:connect).with(@params["host"], @params["port"], Skates::XmppConnection, hash_including("handler" => handler_mock)).and_raise(RuntimeError)
|
21
21
|
lambda {
|
22
|
-
Skates::XmppConnection.
|
22
|
+
Skates::XmppConnection._connect(@params, handler_mock)
|
23
23
|
}.should raise_error(Skates::NotConnected)
|
24
24
|
end
|
25
25
|
|
26
26
|
end
|
27
27
|
|
28
|
+
describe "connect" do
|
29
|
+
describe "connect" do
|
30
|
+
it "should not try to resolve the dns if a host IP has been provided" do
|
31
|
+
@params["host"] = "123.123.123.123"
|
32
|
+
Skates::XmppConnection.should_not_receive(:resolve)
|
33
|
+
Skates::XmppConnection.connect(@params, handler_mock)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "when a host is provided, which is not an IP" do
|
37
|
+
it "should resolve it" do
|
38
|
+
@params["host"] = "domain.tld"
|
39
|
+
Skates::XmppConnection.should_receive(:resolve)
|
40
|
+
Skates::XmppConnection.connect(@params, handler_mock)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "when no host is provided, and no port either" do
|
45
|
+
it "should resolve the host to an IP" do
|
46
|
+
Skates::XmppConnection.should_receive(:resolve)
|
47
|
+
Skates::XmppConnection.connect(@params, handler_mock)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
28
53
|
describe "initialize" do
|
29
54
|
it "should assign @connected to false" do
|
30
55
|
@connection.instance_variable_get("@connected").should be_false
|
@@ -193,5 +218,5 @@ describe Skates::XmppConnection do
|
|
193
218
|
@connection.__send__(:receive_data, data)
|
194
219
|
end
|
195
220
|
end
|
196
|
-
|
221
|
+
|
197
222
|
end
|
@@ -13,4 +13,6 @@ require File.dirname(__FILE__) + "/dependencies"
|
|
13
13
|
Skates::Runner::run(ARGV[0] || "development") do
|
14
14
|
# Run the initializers, too. This is done here since some initializers might need EventMachine to be started.
|
15
15
|
Dir.glob('config/initializers/*.rb').each { |f| require f }
|
16
|
-
end
|
16
|
+
end
|
17
|
+
# Run the destructors, too. They're called when the app exits.
|
18
|
+
Dir.glob('config/destructors/*.rb').each { |f| require f }
|