xmpp4em 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|