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 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