blather 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/blather.png)](https://rubygems.org/gems/blather)
|
4
|
+
[![Build Status](https://secure.travis-ci.org/adhearsion/blather.png?branch=develop)](http://travis-ci.org/adhearsion/blather)
|
5
|
+
[![Dependency Status](https://gemnasium.com/adhearsion/blather.png?travis)](https://gemnasium.com/adhearsion/blather)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/adhearsion/blather.png)](https://codeclimate.com/github/adhearsion/blather)
|
7
|
+
[![Coverage Status](https://coveralls.io/repos/adhearsion/blather/badge.png?branch=develop)](https://coveralls.io/r/adhearsion/blather)
|
8
|
+
[![Inline docs](http://inch-ci.org/github/adhearsion/blather.png?branch=develop)](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
|