xmpp4em 0.2.0
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/History.txt +6 -0
- data/README.md +10 -0
- data/Rakefile +5 -0
- data/examples/component_test.rb +39 -0
- data/examples/stress_test.rb +40 -0
- data/lib/xmpp4em/base_client.rb +122 -0
- data/lib/xmpp4em/base_connection.rb +129 -0
- data/lib/xmpp4em/client.rb +138 -0
- data/lib/xmpp4em/client_connection.rb +24 -0
- data/lib/xmpp4em/component.rb +45 -0
- data/lib/xmpp4em/component_connection.rb +9 -0
- data/lib/xmpp4em.rb +10 -0
- data/spec/spec_runner.rb +26 -0
- data/spec/xmpp4em_spec.rb +57 -0
- metadata +103 -0
data/History.txt
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'xmpp4em'
|
3
|
+
|
4
|
+
started = Time.now
|
5
|
+
users = {}
|
6
|
+
connected = 0
|
7
|
+
|
8
|
+
EM.epoll
|
9
|
+
|
10
|
+
EM.run{
|
11
|
+
user = XMPP4EM::Component.new("test.localhost", 'secret')
|
12
|
+
user.on(:login) do
|
13
|
+
connected += 1
|
14
|
+
p ['connected', "#{connected}"]
|
15
|
+
|
16
|
+
p ['done', Time.now - started]
|
17
|
+
#EM.stop_event_loop
|
18
|
+
end
|
19
|
+
user.on(:iq) do |stanza|
|
20
|
+
p ["IQ",stanza]
|
21
|
+
stanza.to,stanza.from=stanza.from,stanza.to
|
22
|
+
user.send stanza
|
23
|
+
end
|
24
|
+
user.on(:message) do |stanza|
|
25
|
+
p ["message", stanza]
|
26
|
+
stanza.to,stanza.from=stanza.from,stanza.to
|
27
|
+
user.send stanza
|
28
|
+
end
|
29
|
+
user.on(:presence) do |stanza|
|
30
|
+
p ["presence", stanza]
|
31
|
+
stanza.to,stanza.from=stanza.from,stanza.to
|
32
|
+
user.send stanza
|
33
|
+
end
|
34
|
+
user.on(:disconnect) do
|
35
|
+
p ['disconnected']
|
36
|
+
end
|
37
|
+
|
38
|
+
user.connect('127.0.0.1', 5555)
|
39
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'xmpp4em'
|
2
|
+
|
3
|
+
started = Time.now
|
4
|
+
users = {}
|
5
|
+
connected = 0
|
6
|
+
num = Integer(ARGV[0])
|
7
|
+
num = 1000 if num < 1
|
8
|
+
EM.epoll
|
9
|
+
|
10
|
+
EM.run{
|
11
|
+
num.times do |i|
|
12
|
+
users[i] = XMPP4EM::Client.new("test_#{i}@localhost", 'test', :auto_register => true)
|
13
|
+
users[i].on(:login) do
|
14
|
+
connected += 1
|
15
|
+
p ['connected', i, "#{connected} of #{num}"]
|
16
|
+
|
17
|
+
if connected == num
|
18
|
+
p ['done', Time.now - started]
|
19
|
+
EM.stop_event_loop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
users[i].on(:iq) do |stanza|
|
23
|
+
p "IQ"
|
24
|
+
p stanza
|
25
|
+
end
|
26
|
+
users[i].on(:message) do |stanza|
|
27
|
+
p "IQ"
|
28
|
+
p stanza
|
29
|
+
end
|
30
|
+
users[i].on(:presence) do |stanza|
|
31
|
+
p "IQ"
|
32
|
+
p stanza
|
33
|
+
end
|
34
|
+
users[i].on(:disconnect) do
|
35
|
+
p ['disconnected', i]
|
36
|
+
end
|
37
|
+
|
38
|
+
users[i].connect('localhost', 5222)
|
39
|
+
end
|
40
|
+
}
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
require 'stringio'
|
5
|
+
require 'rexml/parsers/sax2parser'
|
6
|
+
|
7
|
+
require 'xmpp4r/idgenerator'
|
8
|
+
require 'xmpp4r/xmppstanza'
|
9
|
+
require 'xmpp4r/iq'
|
10
|
+
require 'xmpp4r/message'
|
11
|
+
require 'xmpp4r/presence'
|
12
|
+
require 'resolv'
|
13
|
+
|
14
|
+
require 'eventmachine'
|
15
|
+
require 'evma_xmlpushparser'
|
16
|
+
EM.epoll
|
17
|
+
|
18
|
+
module XMPP4EM
|
19
|
+
|
20
|
+
class BaseClient
|
21
|
+
include EM::Deferrable
|
22
|
+
|
23
|
+
def initialize user, pass, logger=nil,opts = {}
|
24
|
+
@user = user
|
25
|
+
@pass = pass
|
26
|
+
@logger=logger
|
27
|
+
@deferred_status =:succeeded
|
28
|
+
@connection = nil
|
29
|
+
@authenticated = false
|
30
|
+
@opts = opts
|
31
|
+
@auth_callback = nil
|
32
|
+
@id_callbacks = {}
|
33
|
+
@on_stanza=nil
|
34
|
+
@events_callbacks = {
|
35
|
+
:message => [],
|
36
|
+
:presence => [],
|
37
|
+
:iq => [],
|
38
|
+
:exception => [],
|
39
|
+
:login => [],
|
40
|
+
:disconnect => [],
|
41
|
+
:connected => []
|
42
|
+
}
|
43
|
+
|
44
|
+
on(:disconnect) do
|
45
|
+
@deferred_status = nil
|
46
|
+
@authenticated=false
|
47
|
+
end
|
48
|
+
on(:login) do
|
49
|
+
succeed
|
50
|
+
end
|
51
|
+
end
|
52
|
+
attr_reader :connection, :user
|
53
|
+
|
54
|
+
def reconnect
|
55
|
+
@connection.close_connection_after_writing
|
56
|
+
@deferred_status = nil
|
57
|
+
connect
|
58
|
+
end
|
59
|
+
|
60
|
+
def connected?
|
61
|
+
@connection and !@connection.error?
|
62
|
+
end
|
63
|
+
|
64
|
+
def register_stanza &blk
|
65
|
+
@on_stanza = blk if block_given?
|
66
|
+
end
|
67
|
+
|
68
|
+
def send data, safe=false, &blk
|
69
|
+
|
70
|
+
if block_given? and data.is_a? Jabber::XMPPStanza
|
71
|
+
if data.id.nil?
|
72
|
+
data.id = Jabber::IdGenerator.instance.generate_id
|
73
|
+
end
|
74
|
+
@id_callbacks[ data.id ] = blk
|
75
|
+
end
|
76
|
+
if safe
|
77
|
+
callback { @connection.send(data) }
|
78
|
+
else
|
79
|
+
@connection.send(data)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def close
|
84
|
+
@connection.close_connection_after_writing
|
85
|
+
@deferred_status = nil
|
86
|
+
@connection = nil
|
87
|
+
end
|
88
|
+
alias :disconnect :close
|
89
|
+
|
90
|
+
def receive stanza
|
91
|
+
if stanza.kind_of?(Jabber::XMPPStanza) and stanza.id and blk = @id_callbacks[ stanza.id ]
|
92
|
+
@id_callbacks.delete stanza.id
|
93
|
+
blk.call(stanza)
|
94
|
+
return
|
95
|
+
end
|
96
|
+
|
97
|
+
return if receive_stanza(stanza)
|
98
|
+
return if @on_stanza && @on_stanza.call(stanza)
|
99
|
+
|
100
|
+
case stanza
|
101
|
+
when Jabber::Message then on(:message, stanza)
|
102
|
+
when Jabber::Iq then on(:iq, stanza)
|
103
|
+
when Jabber::Presence then on(:presence, stanza)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def on type, *args, &blk
|
108
|
+
if blk
|
109
|
+
@events_callbacks[type] << blk
|
110
|
+
else
|
111
|
+
@events_callbacks[type].each do |blk|
|
112
|
+
blk.call(*args)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def add_message_callback (&blk) on :message, &blk end
|
118
|
+
def add_presence_callback (&blk) on :presence, &blk end
|
119
|
+
def add_iq_callback (&blk) on :iq, &blk end
|
120
|
+
def on_exception (&blk) on :exception, &blk end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'stringio'
|
3
|
+
require 'rexml/parsers/sax2parser'
|
4
|
+
|
5
|
+
require 'xmpp4r/idgenerator'
|
6
|
+
require 'xmpp4r/xmppstanza'
|
7
|
+
require 'xmpp4r/iq'
|
8
|
+
require 'xmpp4r/message'
|
9
|
+
require 'xmpp4r/presence'
|
10
|
+
|
11
|
+
module XMPP4EM
|
12
|
+
|
13
|
+
class BaseConnection < EventMachine::Connection
|
14
|
+
|
15
|
+
def initialize host, port=5222
|
16
|
+
@host, @port = host, port
|
17
|
+
@client = nil
|
18
|
+
end
|
19
|
+
attr_accessor :client, :host, :port, :logger
|
20
|
+
|
21
|
+
def connection_completed
|
22
|
+
@logger.debug{'connected'} if @logger
|
23
|
+
@stream_features, @stream_mechanisms = {}, []
|
24
|
+
@keepalive = EM::Timer.new(60){ send_data("\n") }
|
25
|
+
@client.on(:connected)
|
26
|
+
init
|
27
|
+
end
|
28
|
+
attr_reader :stream_features
|
29
|
+
|
30
|
+
include EventMachine::XmlPushParser
|
31
|
+
|
32
|
+
def encode2utf8(text)
|
33
|
+
text.respond_to?(:force_encoding) ? text.force_encoding("utf-8") : text
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_element name, attrs
|
37
|
+
e = REXML::Element.new(name)
|
38
|
+
attrs.each { |name, value|
|
39
|
+
e.add_attribute(name, encode2utf8(value))
|
40
|
+
}
|
41
|
+
|
42
|
+
@current = @current.nil? ? e : @current.add_element(e)
|
43
|
+
|
44
|
+
if @current.name == 'stream' and not @started
|
45
|
+
@started = true
|
46
|
+
process
|
47
|
+
@current = nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def end_element name
|
52
|
+
if name == 'stream:stream' and @current.nil?
|
53
|
+
@started = false
|
54
|
+
else
|
55
|
+
if @current.parent
|
56
|
+
@current = @current.parent
|
57
|
+
else
|
58
|
+
process
|
59
|
+
@current = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def characters text
|
65
|
+
@current.text = @current.text.to_s + encode2utf8(text) if @current
|
66
|
+
end
|
67
|
+
|
68
|
+
def error *args
|
69
|
+
p ['error', *args]
|
70
|
+
end
|
71
|
+
|
72
|
+
def receive_data data
|
73
|
+
@logger.debug{"<< #{data}"} if @logger
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
def send data, &blk
|
78
|
+
@logger.debug{ ">> #{data}"} if @logger
|
79
|
+
send_data data.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
def unbind
|
83
|
+
if @keepalive
|
84
|
+
@keepalive.cancel
|
85
|
+
@keepalive = nil
|
86
|
+
end
|
87
|
+
@client.on(:disconnect)
|
88
|
+
@logger.debug{'disconnected'} if @logger
|
89
|
+
end
|
90
|
+
|
91
|
+
def reconnect host = @host, port = @port
|
92
|
+
super
|
93
|
+
end
|
94
|
+
|
95
|
+
def init
|
96
|
+
send "<?xml version='1.0' ?>" unless @started
|
97
|
+
@started = false
|
98
|
+
send "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='#{namespace;}' xml:lang='en' version='1.0' to='#{@host}'>"
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def pre_process_stanza(stanza)
|
104
|
+
end
|
105
|
+
|
106
|
+
def process
|
107
|
+
if @current.namespace('').to_s == '' # REXML namespaces are always strings
|
108
|
+
@current.add_namespace(@streamns)
|
109
|
+
end
|
110
|
+
stanza = @current
|
111
|
+
if 'stream'==stanza.prefix
|
112
|
+
if 'stream'==stanza.name
|
113
|
+
@streamid = stanza.attributes['id']
|
114
|
+
#All connections has the same namespace
|
115
|
+
@streamns = 'jabber:client'
|
116
|
+
end
|
117
|
+
pre_process_stanza(stanza)
|
118
|
+
end
|
119
|
+
# Any stanza, classes are registered by XMPPElement::name_xmlns
|
120
|
+
begin
|
121
|
+
stanza = Jabber::XMPPStanza::import(@current)
|
122
|
+
rescue Jabber::NoNameXmlnsRegistered
|
123
|
+
stanza = @current
|
124
|
+
end
|
125
|
+
@client.receive(stanza)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'xmpp4r/sasl'
|
3
|
+
require 'resolv'
|
4
|
+
|
5
|
+
module XMPP4EM
|
6
|
+
|
7
|
+
class Client < BaseClient
|
8
|
+
|
9
|
+
def initialize user, pass, logger=nil, opts = {}
|
10
|
+
super
|
11
|
+
@opts = { :auto_register => false }.merge(opts)
|
12
|
+
end
|
13
|
+
|
14
|
+
def jid
|
15
|
+
@jid ||= if @user.kind_of?(Jabber::JID)
|
16
|
+
@user
|
17
|
+
else
|
18
|
+
@user =~ /@/ ? Jabber::JID.new(@user) : Jabber::JID.new(@user, 'localhost')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def connect host = jid.domain, port = 5222
|
23
|
+
if host=='localhost' || host=='127.0.0.1' || %r{^([0-9]{1,3}.){3}[0-9]{1,3}$}.match(host)
|
24
|
+
target_host, target_port= host, port
|
25
|
+
else
|
26
|
+
target_host, target_port = resolve_host(host)
|
27
|
+
end
|
28
|
+
EM.connect target_host, target_port, ClientConnection, jid.domain, port do |conn|
|
29
|
+
@connection = conn
|
30
|
+
conn.client = self
|
31
|
+
conn.logger=@logger
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def resolve_host(domain)
|
36
|
+
srv = []
|
37
|
+
begin
|
38
|
+
Resolv::DNS.open { |dns|
|
39
|
+
# If ruby version is too old and SRV is unknown, this will raise a NameError
|
40
|
+
# which is catched below
|
41
|
+
#debug("RESOLVING:\n_xmpp-client._tcp.#{domain} (SRV)")
|
42
|
+
srv = dns.getresources("_xmpp-client._tcp.#{domain}", Resolv::DNS::Resource::IN::SRV)
|
43
|
+
}
|
44
|
+
rescue NameError
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
unless srv.blank?
|
49
|
+
# Sort SRV records: lowest priority first, highest weight first
|
50
|
+
srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) }
|
51
|
+
#debug "USING #{srv.first.target.to_s}"
|
52
|
+
return srv.first.target.to_s, srv.first.port
|
53
|
+
else
|
54
|
+
#debug "USING #{domain}:5222"
|
55
|
+
return domain, 5222
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def login &blk
|
61
|
+
Jabber::SASL::new(self, 'PLAIN').auth(@pass)
|
62
|
+
@auth_callback = blk if block_given?
|
63
|
+
end
|
64
|
+
|
65
|
+
def register &blk
|
66
|
+
reg = Jabber::Iq.new_register(jid.node, @pass)
|
67
|
+
reg.to = jid.domain
|
68
|
+
|
69
|
+
send(reg){ |reply|
|
70
|
+
blk.call( reply.type == :result ? :success : reply.type )
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def send_msg to, msg
|
75
|
+
send_safe Jabber::Message::new(to, msg).set_type(:chat)
|
76
|
+
end
|
77
|
+
|
78
|
+
def receive_stanza(stanza)
|
79
|
+
|
80
|
+
case stanza.name
|
81
|
+
when 'features'
|
82
|
+
unless @authenticated
|
83
|
+
login do |res|
|
84
|
+
# log ['login response', res].inspect
|
85
|
+
if res == :failure and @opts[:auto_register]
|
86
|
+
register do |res|
|
87
|
+
#p ['register response', res]
|
88
|
+
login unless res == :error
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
else
|
94
|
+
if @connection.stream_features.has_key? 'bind'
|
95
|
+
iq = Jabber::Iq.new(:set)
|
96
|
+
bind = iq.add REXML::Element.new('bind')
|
97
|
+
bind.add_namespace @connection.stream_features['bind']
|
98
|
+
resource = bind.add REXML::Element.new('resource')
|
99
|
+
resource.text=jid.resource
|
100
|
+
|
101
|
+
send(iq){ |reply|
|
102
|
+
if reply.type == :result and jid = reply.first_element('//jid') and jid.text
|
103
|
+
# log ['new jid is', jid.text].inspect
|
104
|
+
@jid = Jabber::JID.new(jid.text)
|
105
|
+
end
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
if @connection.stream_features.has_key? 'session'
|
110
|
+
iq = Jabber::Iq.new(:set)
|
111
|
+
session = iq.add REXML::Element.new('session')
|
112
|
+
session.add_namespace @connection.stream_features['session']
|
113
|
+
|
114
|
+
send(iq){ |reply|
|
115
|
+
if reply.type == :result
|
116
|
+
on(:login, stanza)
|
117
|
+
end
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
return true
|
123
|
+
|
124
|
+
when 'success', 'failure'
|
125
|
+
if stanza.name == 'success'
|
126
|
+
@authenticated = true
|
127
|
+
@connection.reset_parser
|
128
|
+
@connection.init
|
129
|
+
end
|
130
|
+
|
131
|
+
@auth_callback.call(stanza.name.to_sym) if @auth_callback
|
132
|
+
return true
|
133
|
+
end
|
134
|
+
false
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
module XMPP4EM
|
4
|
+
|
5
|
+
class ClientConnection < BaseConnection
|
6
|
+
def namespace; 'jabber:client'; end;
|
7
|
+
|
8
|
+
private
|
9
|
+
def pre_process_stanza(stanza)
|
10
|
+
return if 'stream'!=stanza.prefix && 'features'!=stanza.name
|
11
|
+
@stream_features, @stream_mechanisms = {}, []
|
12
|
+
stanza.each do |e|
|
13
|
+
if e.name == 'mechanisms' and e.namespace == 'urn:ietf:params:xml:ns:xmpp-sasl'
|
14
|
+
e.each_element('mechanism') do |mech|
|
15
|
+
@stream_mechanisms.push(mech.text)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
@stream_features[e.name] = e.namespace
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
module XMPP4EM
|
4
|
+
|
5
|
+
class Component < BaseClient
|
6
|
+
def initialize user, pass, opts = {}
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def jid
|
11
|
+
@jid ||= @user.kind_of?(Jabber::JID) ? @user : Jabber::JID.new(@user)
|
12
|
+
end
|
13
|
+
|
14
|
+
def connect host = jid.domain, port = 5222
|
15
|
+
EM.connect host, port, ComponentConnection, jid.domain, port do |conn|
|
16
|
+
@connection = conn
|
17
|
+
conn.client = self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def receive_stanza(stanza)
|
22
|
+
|
23
|
+
case stanza.name
|
24
|
+
when 'stream'
|
25
|
+
if !@authenticated && jid.domain == stanza.attributes['from']
|
26
|
+
streamid = stanza.attributes['id']
|
27
|
+
hash = Digest::SHA1::hexdigest(streamid.to_s + @pass)
|
28
|
+
send("<handshake>#{hash}</handshake>")
|
29
|
+
end
|
30
|
+
return true
|
31
|
+
when 'not-authorized'
|
32
|
+
on(:error, 'not-authorized')
|
33
|
+
return true
|
34
|
+
when 'handshake'
|
35
|
+
@authenticated = true
|
36
|
+
on(:login, stanza)
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
|
40
|
+
false
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/xmpp4em.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module XMPP4EM
|
2
|
+
class NotConnected < Exception; end
|
3
|
+
VERSION='0.2.0'
|
4
|
+
autoload :ClientConnection, 'xmpp4em/client_connection'
|
5
|
+
autoload :Client, 'xmpp4em/client'
|
6
|
+
autoload :BaseClient, 'xmpp4em/base_client'
|
7
|
+
autoload :ComponentConnection, 'xmpp4em/component_connection'
|
8
|
+
autoload :BaseConnection, 'xmpp4em/base_connection'
|
9
|
+
autoload :Component, 'xmpp4em/component'
|
10
|
+
end
|
data/spec/spec_runner.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'bacon'
|
2
|
+
$:.unshift File.dirname(__FILE__) + '/..'
|
3
|
+
require 'xmpp4em'
|
4
|
+
|
5
|
+
shared 'eventmachine' do
|
6
|
+
$bacon_thread = Thread.current
|
7
|
+
def wait
|
8
|
+
Thread.stop
|
9
|
+
@timer = EM::Timer.new(10) do
|
10
|
+
wake
|
11
|
+
should.flunk('waited too long')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
def wake
|
15
|
+
$bacon_thread.wakeup
|
16
|
+
@timer.cancel if @timer
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
EM.run{
|
21
|
+
Thread.new{
|
22
|
+
Thread.abort_on_exception = true
|
23
|
+
require 'xmpp4em_spec'
|
24
|
+
EM.stop_event_loop
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
describe 'XMPP4EM' do
|
2
|
+
behaves_like 'eventmachine'
|
3
|
+
|
4
|
+
@foo = XMPP4EM::Client.new('foo@localhost', 'test', :auto_register => true)
|
5
|
+
@bar = XMPP4EM::Client.new('bar@localhost', 'test', :auto_register => true)
|
6
|
+
|
7
|
+
should 'login to an xmpp server' do
|
8
|
+
@foo.on(:login) do
|
9
|
+
@foo.send Jabber::Presence.new
|
10
|
+
wake
|
11
|
+
end
|
12
|
+
|
13
|
+
@foo.connect
|
14
|
+
wait
|
15
|
+
|
16
|
+
@foo.should.be.connected?
|
17
|
+
end
|
18
|
+
|
19
|
+
should 'send messages to others' do
|
20
|
+
@bar.on(:login) do
|
21
|
+
@bar.send Jabber::Presence.new do
|
22
|
+
wake
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
received = nil
|
27
|
+
@bar.on(:message) do |msg|
|
28
|
+
received = msg.first_element_text('//body')
|
29
|
+
wake
|
30
|
+
end
|
31
|
+
|
32
|
+
@bar.connect
|
33
|
+
wait
|
34
|
+
|
35
|
+
@foo.send_msg 'bar@localhost', 'hello'
|
36
|
+
wait
|
37
|
+
|
38
|
+
received.should == 'hello'
|
39
|
+
end
|
40
|
+
|
41
|
+
should 'fire disconnect callback and reconnect' do
|
42
|
+
user = XMPP4EM::Client.new('user@localhost', 'user', :auto_register => true)
|
43
|
+
user.on(:disconnect){ wake }
|
44
|
+
user.connect 'localhost', 5333 # invalid port
|
45
|
+
wait
|
46
|
+
|
47
|
+
user.should.not.be.connected?
|
48
|
+
|
49
|
+
user.instance_variable_get('@callbacks')[:disconnect] = []
|
50
|
+
user.connection.port = 5222
|
51
|
+
user.on(:login){ wake }
|
52
|
+
user.reconnect
|
53
|
+
wait
|
54
|
+
|
55
|
+
user.should.be.connected?
|
56
|
+
end
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xmpp4em
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Aman Gupta
|
13
|
+
- Kokorin Denis
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-04-21 00:00:00 +04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: eventmachine
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 12
|
31
|
+
- 10
|
32
|
+
version: 0.12.10
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: xmpp4r
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
- 5
|
45
|
+
version: "0.5"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Simple XMPP client and component built on EventMachine.
|
49
|
+
email: mccoder-nospam@ya.ru
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files: []
|
55
|
+
|
56
|
+
files:
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- History.txt
|
60
|
+
- lib/xmpp4em/base_client.rb
|
61
|
+
- lib/xmpp4em/base_connection.rb
|
62
|
+
- lib/xmpp4em/client.rb
|
63
|
+
- lib/xmpp4em/client_connection.rb
|
64
|
+
- lib/xmpp4em/component.rb
|
65
|
+
- lib/xmpp4em/component_connection.rb
|
66
|
+
- lib/xmpp4em.rb
|
67
|
+
- examples/component_test.rb
|
68
|
+
- examples/stress_test.rb
|
69
|
+
- spec/spec_runner.rb
|
70
|
+
- spec/xmpp4em_spec.rb
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/mccoder/xmpp4em
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ~>
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 1
|
86
|
+
- 9
|
87
|
+
version: "1.9"
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.3.6
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: EventMachine based XMPP client and component
|
102
|
+
test_files: []
|
103
|
+
|