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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 821cfa57a4b4d947dfe64d18c05c0bd31b8e33bc
4
- data.tar.gz: 5f4ed8978cbda2d5c422b5334c87bb02c918896b
3
+ metadata.gz: b286a91b3b6c3e092a2e0ebdc9812b44d6f17c50
4
+ data.tar.gz: 126a8aa2caa6063d13dfa8fa32ff4d28f0c12ae0
5
5
  SHA512:
6
- metadata.gz: 69222c73bc14ba562a45e56681e7cfa868a1ed4c6a492ddbee36a8bcaca6abbf3f571e126eab05c5a589143d4151c6ea16bced0d97a0f3c7e86ccdb28d2afce7
7
- data.tar.gz: 92c7c2ba080d2da3143c0fa796c783e5697d3b39079a423aa42295b7eb68b858214c547f7e05d6344967e5effb4925c3e786a24860c05a542703b29e22c21086
6
+ metadata.gz: 2399dc471ce0bd3d4d0121c43b89040069287ae08155cdf90ba509feb19ceac52bfa91f6e1f7c82f372b1d0b85aff38423afc45c35ff695ac0531e685008dccd
7
+ data.tar.gz: e56b81fa5316af92a71b6f2886251130a0e075a6aa762a282249f7f208439602cbfe8fec74d0dd1d982281df96081f8ea64c8c5387e83e3b1b797afa9edbdb17
data/.hound.yml ADDED
@@ -0,0 +1,2 @@
1
+ LineLength:
2
+ Enabled: false
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
@@ -1,4 +1,4 @@
1
- guard 'rspec', :cli => '--format documentation' do
1
+ guard 'rspec', cmd: 'bundle exec rspec' do
2
2
  watch(%r{^spec/.+_spec\.rb$})
3
3
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
4
  watch('spec/spec_helper.rb') { "spec/" }
data/README.md CHANGED
@@ -1,4 +1,11 @@
1
- # Blather [![Build Status](https://travis-ci.org/adhearsion/blather.png?branch=develop)](https://travis-ci.org/adhearsion/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 }
@@ -6,7 +6,7 @@ require 'rubygems'
6
6
  require 'blather/client'
7
7
 
8
8
  when_ready do
9
- roster.grouped.each do |group, items|
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 { self.stream.close_connection_after_writing }
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
- @handler_queue << stanza
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
@@ -95,8 +95,8 @@ class StanzaError < BlatherError
95
95
  # Convert the object to a proper node then convert it to a string
96
96
  #
97
97
  # @return [String]
98
- def to_xml
99
- to_node.to_s
98
+ def to_xml(*args)
99
+ to_node.to_xml(*args)
100
100
  end
101
101
 
102
102
  # @private
@@ -69,8 +69,8 @@ class StreamError < BlatherError
69
69
  # Convert the object to a proper node then convert it to a string
70
70
  #
71
71
  # @return [String]
72
- def to_xml
73
- to_node.to_s
72
+ def to_xml(*args)
73
+ to_node.to_xml(*args)
74
74
  end
75
75
 
76
76
  # @private
@@ -33,11 +33,11 @@ class PubSub
33
33
  #
34
34
  # @return [Blather::XMPPNode]
35
35
  def subscriptions
36
- aff = pubsub.find_first('subscriptions', self.class.registered_ns)
37
- unless aff
38
- (self.pubsub << (aff = XMPPNode.new('subscriptions', self.document)))
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
- aff
40
+ subs
41
41
  end
42
42
 
43
43
  # Iterate over the list of subscriptions
@@ -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
- def self.start(client, jid, pass, host = nil, port = nil, certs_directory = nil, connect_timeout = nil)
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 = nil)
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 => @jid.node,
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#{@jid.node}\x00#{@pass}"))
178
+ @stream.send auth_node('PLAIN', b64("#{@jid.stripped}\x00#{@authcid}\x00#{@pass}"))
178
179
  end
179
180
  end #Plain
180
181
 
@@ -1,3 +1,3 @@
1
1
  module Blather
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -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
- it 'can be setup' do
36
- subject.should respond_to :setup
37
- subject.setup('me@me.com', 'pass').should == subject
38
- end
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
- it 'knows if it has been setup' do
41
- subject.should respond_to :setup?
42
- subject.should_not be_setup
43
- subject.setup 'me@me.com', 'pass'
44
- subject.should be_setup
45
- end
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
- it 'cannot be run before being setup' do
48
- lambda { subject.run }.should raise_error RuntimeError
49
- end
48
+ it 'cannot be run before being setup' do
49
+ lambda { subject.run }.should raise_error RuntimeError
50
+ end
50
51
 
51
- it 'starts up a Component connection when setup without a node' do
52
- setup = 'pubsub.jabber.local', 'secret'
53
- subject.setup *setup
54
- Blather::Stream::Component.expects(:start).with subject, *setup + [nil, nil, nil, nil]
55
- subject.run
56
- end
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
- it 'starts up a Client connection when setup with a node' do
59
- setup = 'test@jabber.local', 'secret'
60
- subject.setup *setup
61
- Blather::Stream::Client.expects(:start).with subject, *setup + [nil, nil, nil, nil]
62
- subject.run
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
- it 'writes to the connection the closes when #close is called' do
85
- stream.expects(:close_connection_after_writing)
86
- EM.stubs(:next_tick).yields
87
- subject.setup 'me.com', 'secret'
88
- subject.post_init stream, Blather::JID.new('me.com')
89
- subject.close
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
@@ -21,7 +21,7 @@ describe Blather::Stream::Parser do
21
21
  def process(*data)
22
22
  client.latch = CountDownLatch.new 1
23
23
  data.each { |d| subject.receive_data d }
24
- client.latch.wait(2).should be_true
24
+ client.latch.wait(2).should be true
25
25
  end
26
26
 
27
27
  def check_parse(data)
@@ -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 be_true
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 be_false
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) { GirlFriday::WorkQueue.immediate! }
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.0.0
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: 2014-02-10 00:00:00.000000000 Z
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.2.0
395
+ rubygems_version: 2.4.5
394
396
  signing_key:
395
397
  specification_version: 4
396
398
  summary: Simpler XMPP built for speed