skates 0.1.11 → 0.2.1
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/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 }
|