blather 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/.hound.yml +2 -0
- data/CHANGELOG.md +5 -0
- data/Guardfile +1 -1
- data/README.md +8 -1
- data/examples/MUC_echo.rb +19 -0
- data/examples/rosterprint.rb +1 -1
- data/lib/blather/client/client.rb +31 -11
- data/lib/blather/errors/stanza_error.rb +2 -2
- data/lib/blather/errors/stream_error.rb +2 -2
- data/lib/blather/stanza/pubsub/subscriptions.rb +4 -4
- data/lib/blather/stream.rb +15 -14
- data/lib/blather/stream/features/sasl.rb +3 -2
- data/lib/blather/version.rb +1 -1
- data/spec/blather/client/client_spec.rb +136 -30
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +9 -0
- data/spec/blather/stream/client_spec.rb +88 -3
- data/spec/blather/stream/component_spec.rb +2 -2
- data/spec/blather/stream/parser_spec.rb +1 -1
- data/spec/blather/stream/ssl_spec.rb +2 -2
- data/spec/blather_spec.rb +0 -4
- data/spec/spec_helper.rb +5 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b286a91b3b6c3e092a2e0ebdc9812b44d6f17c50
|
4
|
+
data.tar.gz: 126a8aa2caa6063d13dfa8fa32ff4d28f0c12ae0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2399dc471ce0bd3d4d0121c43b89040069287ae08155cdf90ba509feb19ceac52bfa91f6e1f7c82f372b1d0b85aff38423afc45c35ff695ac0531e685008dccd
|
7
|
+
data.tar.gz: e56b81fa5316af92a71b6f2886251130a0e075a6aa762a282249f7f208439602cbfe8fec74d0dd1d982281df96081f8ea64c8c5387e83e3b1b797afa9edbdb17
|
data/.hound.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/blather/compare/master...develop)
|
2
2
|
|
3
|
+
# [v1.1.0](https://github.com/adhearsion/blather/compare/v1.0.0...v1.1.0) - [2015-06-12](https://rubygems.org/gems/blather/versions/1.1.0)
|
4
|
+
* Feature: Permit an alternative authentication ID when connection. Used to support [MojoAuth](http://mojoauth.mojolingo.com/) and similar schemes.
|
5
|
+
* Bugfix: Allow sending errors to the wire directly with correct formatting (previously raised)
|
6
|
+
* Bugfix: Don't pass service unavailable response to be processed as roster in client_post_init
|
7
|
+
|
3
8
|
# [v1.0.0](https://github.com/adhearsion/blather/compare/v0.8.8...v1.0.0) - [2014-02-10](https://rubygems.org/gems/blather/versions/1.0.0)
|
4
9
|
* Stable API promise
|
5
10
|
* Bugfix: Fix the DSL module-extended API that was broken by 8327184acc57c20daeaebb975729ff70207eab67
|
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
-
# Blather
|
1
|
+
# Blather
|
2
|
+
|
3
|
+
[](https://rubygems.org/gems/blather)
|
4
|
+
[](http://travis-ci.org/adhearsion/blather)
|
5
|
+
[](https://gemnasium.com/adhearsion/blather)
|
6
|
+
[](https://codeclimate.com/github/adhearsion/blather)
|
7
|
+
[](https://coveralls.io/r/adhearsion/blather)
|
8
|
+
[](http://inch-ci.org/github/adhearsion/blather)
|
2
9
|
|
3
10
|
XMPP DSL (and more) for Ruby written on [EventMachine](http://rubyeventmachine.com/) and [Nokogiri](http://nokogiri.org/).
|
4
11
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'blather/client/dsl'
|
2
|
+
|
3
|
+
module MUC
|
4
|
+
extend Blather::DSL
|
5
|
+
when_ready do
|
6
|
+
puts "Connected ! send messages to #{jid.stripped}."
|
7
|
+
join 'room_name', 'nick_name'
|
8
|
+
end
|
9
|
+
|
10
|
+
message :groupchat?, :body, proc { |m| m.from != jid.stripped }, delay: nil do |m|
|
11
|
+
echo = Blather::Stanza::Message.new
|
12
|
+
echo.to = room
|
13
|
+
echo.body = m.body
|
14
|
+
echo.type = 'groupchat'
|
15
|
+
client.write echo
|
16
|
+
end
|
17
|
+
end
|
18
|
+
MUC.setup 'username', 'password'
|
19
|
+
EM.run { MUC.run }
|
data/examples/rosterprint.rb
CHANGED
@@ -6,7 +6,7 @@ require 'rubygems'
|
|
6
6
|
require 'blather/client'
|
7
7
|
|
8
8
|
when_ready do
|
9
|
-
|
9
|
+
my_roster.grouped.each do |group, items|
|
10
10
|
puts "#{'*'*3} #{group || 'Ungrouped'} #{'*'*3}"
|
11
11
|
items.each { |item| puts "- #{item.name} (#{item.jid})" }
|
12
12
|
puts
|
@@ -34,7 +34,8 @@ module Blather
|
|
34
34
|
class Client
|
35
35
|
attr_reader :jid,
|
36
36
|
:roster,
|
37
|
-
:caps
|
37
|
+
:caps,
|
38
|
+
:queue_size
|
38
39
|
|
39
40
|
# Create a new client and set it up
|
40
41
|
#
|
@@ -43,12 +44,17 @@ module Blather
|
|
43
44
|
# @param [String] host if this isn't set it'll be resolved off the JID's
|
44
45
|
# domain
|
45
46
|
# @param [Fixnum, String] port the port to connect to.
|
47
|
+
# @param [Hash] options a list of options to create the client with
|
48
|
+
# @option options [Number] :workqueue_count (5) the number of threads used to process incoming XMPP messages.
|
49
|
+
# If this parameter is specified with 0, no background threads are used;
|
50
|
+
# instead stanzas are handled in the same process that the Client is running in.
|
46
51
|
#
|
47
52
|
# @return [Blather::Client]
|
48
|
-
def self.setup(jid, password, host = nil, port = nil, certs = nil, connect_timeout = nil)
|
49
|
-
self.new.setup(jid, password, host, port, certs, connect_timeout)
|
53
|
+
def self.setup(jid, password, host = nil, port = nil, certs = nil, connect_timeout = nil, options = {})
|
54
|
+
self.new.setup(jid, password, host, port, certs, connect_timeout, options)
|
50
55
|
end
|
51
56
|
|
57
|
+
|
52
58
|
def initialize # @private
|
53
59
|
@state = :initializing
|
54
60
|
|
@@ -58,10 +64,7 @@ module Blather
|
|
58
64
|
@filters = {:before => [], :after => []}
|
59
65
|
@roster = Roster.new self
|
60
66
|
@caps = Stanza::Capabilities.new
|
61
|
-
|
62
|
-
@handler_queue = GirlFriday::WorkQueue.new :handle_stanza, :size => 5 do |stanza|
|
63
|
-
handle_data stanza
|
64
|
-
end
|
67
|
+
@queue_size = 5
|
65
68
|
|
66
69
|
setup_initial_handlers
|
67
70
|
end
|
@@ -168,7 +171,11 @@ module Blather
|
|
168
171
|
|
169
172
|
# Close the connection
|
170
173
|
def close
|
171
|
-
EM.next_tick {
|
174
|
+
EM.next_tick {
|
175
|
+
handler_queue.shutdown if handler_queue
|
176
|
+
@handler_queue = nil
|
177
|
+
self.stream.close_connection_after_writing if connected?
|
178
|
+
}
|
172
179
|
end
|
173
180
|
|
174
181
|
# @private
|
@@ -185,7 +192,11 @@ module Blather
|
|
185
192
|
|
186
193
|
# @private
|
187
194
|
def receive_data(stanza)
|
188
|
-
|
195
|
+
if handler_queue
|
196
|
+
handler_queue << stanza
|
197
|
+
else
|
198
|
+
handle_data stanza
|
199
|
+
end
|
189
200
|
end
|
190
201
|
|
191
202
|
def handle_data(stanza)
|
@@ -202,16 +213,25 @@ module Blather
|
|
202
213
|
end
|
203
214
|
|
204
215
|
# @private
|
205
|
-
def setup(jid, password, host = nil, port = nil, certs = nil, connect_timeout = nil)
|
216
|
+
def setup(jid, password, host = nil, port = nil, certs = nil, connect_timeout = nil, options = {})
|
206
217
|
@jid = JID.new(jid)
|
207
218
|
@setup = [@jid, password]
|
208
219
|
@setup << host
|
209
220
|
@setup << port
|
210
221
|
@setup << certs
|
211
222
|
@setup << connect_timeout
|
223
|
+
@queue_size = options[:workqueue_count] || 5
|
212
224
|
self
|
213
225
|
end
|
214
226
|
|
227
|
+
# @private
|
228
|
+
def handler_queue
|
229
|
+
return if queue_size == 0
|
230
|
+
@handler_queue ||= GirlFriday::WorkQueue.new :handle_stanza, :size => queue_size do |stanza|
|
231
|
+
handle_data stanza
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
215
235
|
protected
|
216
236
|
|
217
237
|
def stream
|
@@ -257,7 +277,7 @@ module Blather
|
|
257
277
|
|
258
278
|
def client_post_init
|
259
279
|
write_with_handler Stanza::Iq::Roster.new do |node|
|
260
|
-
roster.process node
|
280
|
+
roster.process(node) unless node.error?
|
261
281
|
write @status
|
262
282
|
ready!
|
263
283
|
end
|
@@ -33,11 +33,11 @@ class PubSub
|
|
33
33
|
#
|
34
34
|
# @return [Blather::XMPPNode]
|
35
35
|
def subscriptions
|
36
|
-
|
37
|
-
unless
|
38
|
-
|
36
|
+
subs = pubsub.find_first('ns:subscriptions', :ns => self.class.registered_ns)
|
37
|
+
unless subs
|
38
|
+
self.pubsub << (subs = XMPPNode.new('subscriptions', self.document))
|
39
39
|
end
|
40
|
-
|
40
|
+
subs
|
41
41
|
end
|
42
42
|
|
43
43
|
# Iterate over the list of subscriptions
|
data/lib/blather/stream.rb
CHANGED
@@ -56,7 +56,7 @@ module Blather
|
|
56
56
|
# @private
|
57
57
|
STREAM_NS = 'http://etherx.jabber.org/streams'
|
58
58
|
attr_accessor :password
|
59
|
-
attr_reader :jid
|
59
|
+
attr_reader :jid, :authcid
|
60
60
|
|
61
61
|
# Start the stream between client and server
|
62
62
|
#
|
@@ -71,14 +71,17 @@ module Blather
|
|
71
71
|
# @param [String, nil] certs the trusted cert store in pem format to verify
|
72
72
|
# communication with the server is trusted.
|
73
73
|
# @param [Fixnum, nil] connect_timeout the number of seconds for which to wait for a successful connection
|
74
|
-
|
74
|
+
# @param [Hash] opts options for modifying the connection
|
75
|
+
# @options opts [String] :authcid The authentication ID, which defaults to the node part of the specified JID
|
76
|
+
def self.start(client, jid, pass, host = nil, port = nil, certs_directory = nil, connect_timeout = nil, opts = {})
|
75
77
|
jid = JID.new jid
|
76
78
|
port ||= 5222
|
77
79
|
if certs_directory
|
78
80
|
@store = CertStore.new(certs_directory)
|
79
81
|
end
|
82
|
+
authcid = opts[:authcid]
|
80
83
|
if host
|
81
|
-
connect host, port, self, client, jid, pass, connect_timeout
|
84
|
+
connect host, port, self, client, jid, pass, connect_timeout, authcid
|
82
85
|
else
|
83
86
|
require 'resolv'
|
84
87
|
srv = []
|
@@ -90,7 +93,7 @@ module Blather
|
|
90
93
|
end
|
91
94
|
|
92
95
|
if srv.empty?
|
93
|
-
connect jid.domain, port, self, client, jid, pass, connect_timeout
|
96
|
+
connect jid.domain, port, self, client, jid, pass, connect_timeout, authcid
|
94
97
|
else
|
95
98
|
srv.sort! do |a,b|
|
96
99
|
(a.priority != b.priority) ? (a.priority <=> b.priority) :
|
@@ -98,7 +101,7 @@ module Blather
|
|
98
101
|
end
|
99
102
|
|
100
103
|
srv.detect do |r|
|
101
|
-
not connect(r.target.to_s, r.port, self, client, jid, pass, connect_timeout) === false
|
104
|
+
not connect(r.target.to_s, r.port, self, client, jid, pass, connect_timeout, authcid) === false
|
102
105
|
end
|
103
106
|
end
|
104
107
|
end
|
@@ -108,8 +111,8 @@ module Blather
|
|
108
111
|
# Stream will raise +NoConnection+ if it receives #unbind before #post_init
|
109
112
|
# this catches that and returns false prompting for another attempt
|
110
113
|
# @private
|
111
|
-
def self.connect(host, port, conn, client, jid, pass, connect_timeout
|
112
|
-
EM.connect host, port, conn, client, jid, pass, connect_timeout
|
114
|
+
def self.connect(host, port, conn, client, jid, pass, connect_timeout, authcid)
|
115
|
+
EM.connect host, port, conn, client, jid, pass, connect_timeout, authcid
|
113
116
|
rescue NoConnection
|
114
117
|
false
|
115
118
|
end
|
@@ -131,7 +134,7 @@ module Blather
|
|
131
134
|
|
132
135
|
# Called by EM.connect to initialize stream variables
|
133
136
|
# @private
|
134
|
-
def initialize(client, jid, pass, connect_timeout = nil)
|
137
|
+
def initialize(client, jid, pass, connect_timeout = nil, authcid = nil)
|
135
138
|
super()
|
136
139
|
|
137
140
|
@error = nil
|
@@ -141,6 +144,7 @@ module Blather
|
|
141
144
|
@to = self.jid.domain
|
142
145
|
@password = pass
|
143
146
|
@connect_timeout = connect_timeout || 180
|
147
|
+
@authcid = authcid || self.jid.node
|
144
148
|
end
|
145
149
|
|
146
150
|
# Called when EM completes the connection to the server
|
@@ -189,20 +193,17 @@ module Blather
|
|
189
193
|
# Called by EM when the connection is closed
|
190
194
|
# @private
|
191
195
|
def unbind
|
192
|
-
cleanup
|
193
|
-
|
194
196
|
raise NoConnection unless @inited
|
195
197
|
raise ConnectionFailed unless @connected
|
196
198
|
|
199
|
+
@parser.finish
|
200
|
+
|
201
|
+
@connect_timer.cancel if @connect_timer
|
197
202
|
@state = :stopped
|
198
203
|
@client.receive_data @error if @error
|
199
204
|
@client.unbind
|
200
205
|
end
|
201
206
|
|
202
|
-
def cleanup
|
203
|
-
@connect_timer.cancel if @connect_timer
|
204
|
-
end
|
205
|
-
|
206
207
|
# Called by the parser with parsed nodes
|
207
208
|
# @private
|
208
209
|
def receive(node)
|
@@ -20,6 +20,7 @@ class Stream
|
|
20
20
|
super
|
21
21
|
@jid = @stream.jid
|
22
22
|
@pass = @stream.password
|
23
|
+
@authcid = @stream.authcid
|
23
24
|
@mechanisms = []
|
24
25
|
end
|
25
26
|
|
@@ -145,7 +146,7 @@ class Stream
|
|
145
146
|
@response = {
|
146
147
|
:nonce => @nonce,
|
147
148
|
:charset => 'utf-8',
|
148
|
-
:username => @
|
149
|
+
:username => @authcid,
|
149
150
|
:realm => @realm || @jid.domain,
|
150
151
|
:cnonce => h(Time.new.to_f.to_s),
|
151
152
|
:nc => '00000001',
|
@@ -174,7 +175,7 @@ class Stream
|
|
174
175
|
# @private
|
175
176
|
module Plain
|
176
177
|
def authenticate
|
177
|
-
@stream.send auth_node('PLAIN', b64("#{@jid.stripped}\x00#{@
|
178
|
+
@stream.send auth_node('PLAIN', b64("#{@jid.stripped}\x00#{@authcid}\x00#{@pass}"))
|
178
179
|
end
|
179
180
|
end #Plain
|
180
181
|
|
data/lib/blather/version.rb
CHANGED
@@ -32,34 +32,74 @@ describe Blather::Client do
|
|
32
32
|
subject.caps.should be_kind_of Blather::Stanza::Capabilities
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
describe '#setup' do
|
36
|
+
it 'can be setup' do
|
37
|
+
subject.should respond_to :setup
|
38
|
+
subject.setup('me@me.com', 'pass').should == subject
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
it 'knows if it has been setup' do
|
42
|
+
subject.should respond_to :setup?
|
43
|
+
subject.should_not be_setup
|
44
|
+
subject.setup 'me@me.com', 'pass'
|
45
|
+
subject.should be_setup
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
it 'cannot be run before being setup' do
|
49
|
+
lambda { subject.run }.should raise_error RuntimeError
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
it 'starts up a Component connection when setup without a node' do
|
53
|
+
setup = 'pubsub.jabber.local', 'secret'
|
54
|
+
subject.setup *setup
|
55
|
+
Blather::Stream::Component.expects(:start).with subject, *setup + [nil, nil, nil, nil]
|
56
|
+
subject.run
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'starts up a Client connection when setup with a node' do
|
60
|
+
setup = 'test@jabber.local', 'secret'
|
61
|
+
subject.setup *setup
|
62
|
+
Blather::Stream::Client.expects(:start).with subject, *setup + [nil, nil, nil, nil]
|
63
|
+
subject.run
|
64
|
+
end
|
65
|
+
|
66
|
+
context "setting queue size" do
|
67
|
+
let(:jid) { 'test@jabber.local' }
|
68
|
+
let(:password) { 'secret' }
|
69
|
+
let(:queue_size) { 3 }
|
70
|
+
|
71
|
+
subject { Blather::Client.setup(jid, password, nil, nil, nil, nil, :workqueue_count => queue_size) }
|
72
|
+
|
73
|
+
it 'sets the queue size on the client' do
|
74
|
+
subject.queue_size.should == queue_size
|
75
|
+
end
|
57
76
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
77
|
+
describe 'receiving data' do
|
78
|
+
let(:stanza) { Blather::Stanza::Iq.new }
|
79
|
+
|
80
|
+
context 'when the queue size is 0' do
|
81
|
+
let(:queue_size) { 0 }
|
82
|
+
|
83
|
+
it "has no handler queue" do
|
84
|
+
subject.handler_queue.should be_nil
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'handles the data immediately' do
|
88
|
+
subject.expects(:handle_data).with(stanza)
|
89
|
+
subject.receive_data stanza
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when the queue size is non-zero' do
|
94
|
+
let(:queue_size) { 4 }
|
95
|
+
|
96
|
+
it 'enqueues the data on the handler queue' do
|
97
|
+
subject.handler_queue.expects(:<<).with(stanza)
|
98
|
+
subject.receive_data stanza
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
63
103
|
end
|
64
104
|
|
65
105
|
it 'knows if it is disconnected' do
|
@@ -81,12 +121,57 @@ describe Blather::Client do
|
|
81
121
|
end
|
82
122
|
end
|
83
123
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
124
|
+
describe '#close' do
|
125
|
+
before do
|
126
|
+
EM.stubs(:next_tick).yields
|
127
|
+
subject.setup 'me.com', 'secret'
|
128
|
+
end
|
129
|
+
|
130
|
+
context "without a setup stream" do
|
131
|
+
it "does not close the connection" do
|
132
|
+
stream.expects(:close_connection_after_writing).never
|
133
|
+
subject.close
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when a stream is setup" do
|
138
|
+
let(:stream_stopped) { false }
|
139
|
+
before do
|
140
|
+
subject.post_init stream, Blather::JID.new('me.com')
|
141
|
+
stream.stubs(:stopped? => stream_stopped)
|
142
|
+
end
|
143
|
+
|
144
|
+
context "when the stream is stopped" do
|
145
|
+
let(:stream_stopped) { true }
|
146
|
+
|
147
|
+
it "does not close the connection, since it's already closed" do
|
148
|
+
stream.expects(:close_connection_after_writing).never
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'writes to the connection the closes when #close is called' do
|
153
|
+
stream.expects(:close_connection_after_writing)
|
154
|
+
subject.close
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'shuts down the workqueue' do
|
158
|
+
stream.stubs(:close_connection_after_writing)
|
159
|
+
subject.handler_queue.expects(:shutdown)
|
160
|
+
subject.close
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'forces the work queue to be re-created when referenced' do
|
164
|
+
stream.stubs(:close_connection_after_writing)
|
165
|
+
subject.close
|
166
|
+
|
167
|
+
fake_queue = stub('GirlFriday::WorkQueue')
|
168
|
+
GirlFriday::WorkQueue.expects(:new)
|
169
|
+
.with(:handle_stanza, :size => subject.queue_size)
|
170
|
+
.returns(fake_queue)
|
171
|
+
|
172
|
+
subject.handler_queue.should == fake_queue
|
173
|
+
end
|
174
|
+
end
|
90
175
|
end
|
91
176
|
|
92
177
|
it 'shuts down EM when #unbind is called if it is running' do
|
@@ -376,6 +461,27 @@ describe Blather::Client do
|
|
376
461
|
subject.register_handler(:ready) { ready.call }
|
377
462
|
subject.post_init stream, Blather::JID.new('n@d/r')
|
378
463
|
end
|
464
|
+
|
465
|
+
it 'gracefully handles service unavailability upon requesting the roster' do
|
466
|
+
result_roster = Blather::Stanza::Iq.parse <<-XML
|
467
|
+
<iq type="error" to="n@d/r">
|
468
|
+
<error type="cancel">
|
469
|
+
<service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
|
470
|
+
</error>
|
471
|
+
</iq>
|
472
|
+
XML
|
473
|
+
|
474
|
+
stream.stubs(:send).with do |s|
|
475
|
+
result_roster.id = s.id
|
476
|
+
subject.receive_data result_roster
|
477
|
+
true
|
478
|
+
end
|
479
|
+
|
480
|
+
ready = mock
|
481
|
+
ready.expects(:call)
|
482
|
+
subject.register_handler(:ready) { ready.call }
|
483
|
+
subject.post_init stream, Blather::JID.new('n@d/r')
|
484
|
+
end
|
379
485
|
end
|
380
486
|
|
381
487
|
describe 'filters' do
|
@@ -31,6 +31,15 @@ describe Blather::Stanza::PubSub::Subscriptions do
|
|
31
31
|
subscriptions.find('//ns:pubsub/ns:subscriptions', :ns => Blather::Stanza::PubSub.registered_ns).should_not be_empty
|
32
32
|
end
|
33
33
|
|
34
|
+
it 'ensures the subscriptions node is not duplicated when calling #subscriptions' do
|
35
|
+
subscriptions = Blather::Stanza::PubSub::Subscriptions.new
|
36
|
+
subscriptions.pubsub.remove_children :subscriptions
|
37
|
+
subscriptions.find('//ns:pubsub/ns:subscriptions', :ns => Blather::Stanza::PubSub.registered_ns).should be_empty
|
38
|
+
|
39
|
+
5.times { subscriptions.subscriptions }
|
40
|
+
subscriptions.find('//ns:pubsub/ns:subscriptions', :ns => Blather::Stanza::PubSub.registered_ns).count.should eq(1)
|
41
|
+
end
|
42
|
+
|
34
43
|
it 'defaults to a get node' do
|
35
44
|
aff = Blather::Stanza::PubSub::Subscriptions.new
|
36
45
|
aff.type.should == :get
|
@@ -5,6 +5,7 @@ describe Blather::Stream::Client do
|
|
5
5
|
let(:client) { mock 'Client' }
|
6
6
|
let(:server_port) { 50000 - rand(1000) }
|
7
7
|
let(:jid) { Blather::JID.new 'n@d/r' }
|
8
|
+
let(:authcid) { nil }
|
8
9
|
|
9
10
|
before do
|
10
11
|
[:unbind, :post_init, :jid=].each do |m|
|
@@ -23,12 +24,10 @@ describe Blather::Stream::Client do
|
|
23
24
|
EventMachine::start_server '127.0.0.1', server_port, ServerMock
|
24
25
|
|
25
26
|
# Blather::Stream connection
|
26
|
-
EM.connect('127.0.0.1', server_port, Blather::Stream::Client, client, jid, 'pass') { |c| @stream = c }
|
27
|
+
EM.connect('127.0.0.1', server_port, Blather::Stream::Client, client, jid, 'pass', nil, authcid) { |c| @stream = c }
|
27
28
|
}
|
28
29
|
end
|
29
30
|
|
30
|
-
after { sleep 0.1; @stream.cleanup if @stream }
|
31
|
-
|
32
31
|
it 'can be started' do
|
33
32
|
params = [client, 'n@d/r', 'pass', 'host', 1234]
|
34
33
|
EM.expects(:connect).with do |*parms|
|
@@ -134,6 +133,7 @@ describe Blather::Stream::Client do
|
|
134
133
|
val.should match(/stream:stream/)
|
135
134
|
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
136
135
|
server.send_data "<message to='a@b/c' from='d@e/f' type='chat' xml:lang='en'><body>Message!</body></message>"
|
136
|
+
server.send_data "</stream:stream>"
|
137
137
|
true
|
138
138
|
end
|
139
139
|
end
|
@@ -430,6 +430,76 @@ describe Blather::Stream::Client do
|
|
430
430
|
end
|
431
431
|
end
|
432
432
|
|
433
|
+
context "with an alternative authcid specified" do
|
434
|
+
let(:authcid) { 'doo' }
|
435
|
+
|
436
|
+
it 'connects via SASL MD5 when asked' do
|
437
|
+
Time.any_instance.stubs(:to_f).returns(1.1)
|
438
|
+
|
439
|
+
state = nil
|
440
|
+
mocked_server(5) do |val, server|
|
441
|
+
case state
|
442
|
+
when nil
|
443
|
+
state = :started
|
444
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism></mechanisms></stream:features>"
|
445
|
+
val.should match(/stream:stream/)
|
446
|
+
|
447
|
+
when :started
|
448
|
+
state = :auth_sent
|
449
|
+
server.send_data "<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==</challenge>"
|
450
|
+
val.should match(/auth.*DIGEST\-MD5/)
|
451
|
+
|
452
|
+
when :auth_sent
|
453
|
+
state = :response1_sent
|
454
|
+
server.send_data "<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=</challenge>"
|
455
|
+
val.should ==('<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">bm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixjaGFyc2V0PXV0Zi04LHVzZXJuYW1lPSJkb28iLHJlYWxtPSJzb21lcmVhbG0iLGNub25jZT0iNzc3ZDQ1YmJiY2RmNTBkNDljNDJjNzBhZDdhY2Y1ZmUiLG5jPTAwMDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZCIscmVzcG9uc2U9YzBhMzQ4MDkyOWJmMDFiMWUyODc0NTE1YWQ5ZjNlYzE=</response>')
|
456
|
+
|
457
|
+
when :response1_sent
|
458
|
+
state = :response2_sent
|
459
|
+
server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
|
460
|
+
val.should match(%r{<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"\s?/>})
|
461
|
+
|
462
|
+
when :response2_sent
|
463
|
+
EM.stop
|
464
|
+
state = :complete
|
465
|
+
val.should match(/stream:stream/)
|
466
|
+
|
467
|
+
else
|
468
|
+
EM.stop
|
469
|
+
false
|
470
|
+
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
it 'will connect via SSL PLAIN when asked' do
|
476
|
+
state = nil
|
477
|
+
mocked_server(3) do |val, server|
|
478
|
+
case state
|
479
|
+
when nil
|
480
|
+
state = :started
|
481
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</mechanism></mechanisms></stream:features>"
|
482
|
+
val.should match(/stream:stream/)
|
483
|
+
|
484
|
+
when :started
|
485
|
+
state = :auth_sent
|
486
|
+
server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
|
487
|
+
Nokogiri::XML(val).to_xml.should == Nokogiri::XML('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">bkBkAGRvbwBwYXNz</auth>').to_xml
|
488
|
+
|
489
|
+
when :auth_sent
|
490
|
+
EM.stop
|
491
|
+
state = :complete
|
492
|
+
val.should match(/stream:stream/)
|
493
|
+
|
494
|
+
else
|
495
|
+
EM.stop
|
496
|
+
false
|
497
|
+
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
433
503
|
it 'will connect via SSL ANONYMOUS when asked' do
|
434
504
|
state = nil
|
435
505
|
mocked_server(3) do |val, server|
|
@@ -1057,6 +1127,21 @@ describe Blather::Stream::Client do
|
|
1057
1127
|
comp.send msg
|
1058
1128
|
end
|
1059
1129
|
|
1130
|
+
it 'sends stanza errors to the wire correctly' do
|
1131
|
+
stanza = Blather::Stanza::Iq.new :set, 'foo@bar.com', '123'
|
1132
|
+
error = Blather::StanzaError.new(stanza, 'registration-required', :cancel)
|
1133
|
+
comp = Blather::Stream::Client.new nil, client, 'node@jid.com/resource', 'pass'
|
1134
|
+
comp.expects(:send_data).with { |s| s.should match(/<error type=\"cancel\"><registration-required/); true }
|
1135
|
+
comp.send error
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
it 'sends stream errors to the wire correctly' do
|
1139
|
+
error = Blather::StreamError.new('foo-error')
|
1140
|
+
comp = Blather::Stream::Client.new nil, client, 'node@jid.com/resource', 'pass'
|
1141
|
+
comp.expects(:send_data).with { |s| s.should match(/<stream:error xmlns:stream=\"http:\/\/etherx.jabber.org\/streams\"><foo-error/); true }
|
1142
|
+
comp.send error
|
1143
|
+
end
|
1144
|
+
|
1060
1145
|
it 'sends xml without formatting' do
|
1061
1146
|
msg = Blather::Stanza::Message.new 'to@jid.com', 'body'
|
1062
1147
|
msg.xhtml = '<i>xhtml</i> body'
|
@@ -23,8 +23,6 @@ describe Blather::Stream::Component do
|
|
23
23
|
}
|
24
24
|
end
|
25
25
|
|
26
|
-
after { sleep 0.1; @stream.cleanup if @stream }
|
27
|
-
|
28
26
|
it 'can be started' do
|
29
27
|
params = [client, 'comp.id', 'secret', 'host', 1234]
|
30
28
|
EM.expects(:connect).with do |*parms|
|
@@ -70,6 +68,8 @@ describe Blather::Stream::Component do
|
|
70
68
|
end
|
71
69
|
|
72
70
|
it 'sends stanzas to the client when the stream is ready' do
|
71
|
+
pending "This hangs the test suite" if jruby?
|
72
|
+
|
73
73
|
client.stubs :post_init
|
74
74
|
client.expects(:receive_data).with do |n|
|
75
75
|
EM.stop
|
@@ -10,7 +10,7 @@ describe Blather::CertStore do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'can verify valid cert' do
|
13
|
-
subject.trusted?(cert).should
|
13
|
+
subject.trusted?(cert).should be true
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'can verify invalid cert' do
|
@@ -19,6 +19,6 @@ describe Blather::CertStore do
|
|
19
19
|
|
20
20
|
it 'cannot verify when the cert authority is not trusted' do
|
21
21
|
@store = Blather::CertStore.new("../")
|
22
|
-
@store.trusted?(cert).should
|
22
|
+
@store.trusted?(cert).should be false
|
23
23
|
end
|
24
24
|
end
|
data/spec/blather_spec.rb
CHANGED
@@ -6,10 +6,6 @@ describe Blather do
|
|
6
6
|
it "should return a Logger instance" do
|
7
7
|
Blather.logger.should be_instance_of Logger
|
8
8
|
end
|
9
|
-
|
10
|
-
it "should config log level to info by default" do
|
11
|
-
Blather.logger.level.should == 1
|
12
|
-
end
|
13
9
|
end
|
14
10
|
|
15
11
|
describe "while using the log method" do
|
data/spec/spec_helper.rb
CHANGED
@@ -8,7 +8,11 @@ RSpec.configure do |config|
|
|
8
8
|
config.filter_run :focus => true
|
9
9
|
config.run_all_when_everything_filtered = true
|
10
10
|
|
11
|
-
config.before(:each)
|
11
|
+
config.before(:each) do
|
12
|
+
GirlFriday::WorkQueue.immediate!
|
13
|
+
Blather::Stream::Parser.debug = true
|
14
|
+
Blather.logger = Logger.new($stdout).tap { |logger| logger.level = Logger::DEBUG }
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
def parse_stanza(xml)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blather
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Smick
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-06-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -222,6 +222,7 @@ extra_rdoc_files:
|
|
222
222
|
- README.md
|
223
223
|
files:
|
224
224
|
- ".gitignore"
|
225
|
+
- ".hound.yml"
|
225
226
|
- ".rspec"
|
226
227
|
- ".travis.yml"
|
227
228
|
- CHANGELOG.md
|
@@ -231,6 +232,7 @@ files:
|
|
231
232
|
- README.md
|
232
233
|
- Rakefile
|
233
234
|
- blather.gemspec
|
235
|
+
- examples/MUC_echo.rb
|
234
236
|
- examples/certs/README
|
235
237
|
- examples/certs/ca-bundle.crt
|
236
238
|
- examples/echo.rb
|
@@ -390,7 +392,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
390
392
|
version: '0'
|
391
393
|
requirements: []
|
392
394
|
rubyforge_project:
|
393
|
-
rubygems_version: 2.
|
395
|
+
rubygems_version: 2.4.5
|
394
396
|
signing_key:
|
395
397
|
specification_version: 4
|
396
398
|
summary: Simpler XMPP built for speed
|