jabber4r-revive 0.9.0 → 0.10.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.
Files changed (45) hide show
  1. data/.gitignore +5 -4
  2. data/.rspec +3 -3
  3. data/.travis.yml +7 -7
  4. data/CHANGELOG +11 -1
  5. data/Gemfile +3 -3
  6. data/README.md +29 -29
  7. data/Rakefile +70 -70
  8. data/jabber4r-revive.gemspec +25 -25
  9. data/lib/jabber4r.rb +38 -33
  10. data/lib/jabber4r/bosh.rb +21 -0
  11. data/lib/jabber4r/bosh/authentication.rb +13 -0
  12. data/lib/jabber4r/bosh/authentication/non_sasl.rb +219 -0
  13. data/lib/jabber4r/bosh/authentication/sasl.rb +239 -0
  14. data/lib/jabber4r/bosh/session.rb +144 -0
  15. data/lib/jabber4r/connection.rb +259 -258
  16. data/lib/jabber4r/debugger.rb +60 -60
  17. data/lib/jabber4r/jid.rb +20 -19
  18. data/lib/jabber4r/protocol.rb +249 -257
  19. data/lib/jabber4r/protocol/authentication.rb +14 -0
  20. data/lib/jabber4r/protocol/authentication/non_sasl.rb +138 -0
  21. data/lib/jabber4r/protocol/authentication/sasl.rb +88 -0
  22. data/lib/jabber4r/protocol/iq.rb +259 -259
  23. data/lib/jabber4r/protocol/message.rb +245 -245
  24. data/lib/jabber4r/protocol/parsed_xml_element.rb +207 -207
  25. data/lib/jabber4r/protocol/presence.rb +160 -160
  26. data/lib/jabber4r/protocol/xml_element.rb +143 -143
  27. data/lib/jabber4r/rexml_1.8_patch.rb +15 -15
  28. data/lib/jabber4r/roster.rb +38 -38
  29. data/lib/jabber4r/session.rb +615 -615
  30. data/lib/jabber4r/version.rb +10 -3
  31. data/spec/lib/jabber4r/bosh/authentication/non_sasl_spec.rb +79 -0
  32. data/spec/lib/jabber4r/bosh/authentication/sasl_spec.rb +42 -0
  33. data/spec/lib/jabber4r/bosh/session_spec.rb +406 -0
  34. data/spec/lib/jabber4r/bosh_spec.rb +0 -0
  35. data/spec/lib/jabber4r/connection_spec.rb +174 -174
  36. data/spec/lib/jabber4r/debugger_spec.rb +35 -35
  37. data/spec/lib/jabber4r/jid_spec.rb +197 -197
  38. data/spec/lib/jabber4r/protocol/authentication/non_sasl_spec.rb +79 -0
  39. data/spec/lib/jabber4r/protocol/authentication/sasl_spec.rb +42 -0
  40. data/spec/spec_helper.rb +11 -11
  41. data/spec/support/mocks/tcp_socket_mock.rb +8 -8
  42. metadata +61 -45
  43. data/Gemfile.lock +0 -45
  44. data/lib/jabber4r/bosh_session.rb +0 -224
  45. data/spec/lib/jabber4r/bosh_session_spec.rb +0 -150
@@ -1,3 +1,10 @@
1
- module Jabber
2
- VERSION = "0.9.0"
3
- end
1
+ # coding: utf-8
2
+
3
+ # License: see LICENSE
4
+ # Jabber4R - Jabber Instant Messaging Library for Ruby
5
+ # Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
6
+ # Copyright (C) 2013 Sergey Fedorov <strech_ftf@mail.ru>
7
+
8
+ module Jabber
9
+ VERSION = "0.10.0"
10
+ end
@@ -0,0 +1,79 @@
1
+ # coding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Jabber::Bosh::Authentication::NonSASL::Query do
5
+ let(:jid) { Jabber::JID.new("strech@localhost/my-resource") }
6
+
7
+ describe "#initialize" do
8
+ context "when create plain non-sasl authentication" do
9
+ let(:auth) { described_class.new(jid, "my-password") }
10
+
11
+ it { expect(auth.stream_id).to be_nil }
12
+ end
13
+
14
+ context "when jid is a String" do
15
+ let(:auth) { described_class.new("hello@localhost/my-res", "my-password") }
16
+
17
+ it { expect { auth }.not_to raise_error TypeError }
18
+ end
19
+
20
+ context "when jid is not instance of Jabber::JID or String" do
21
+ let(:auth) { described_class.new(:hello, "my-password") }
22
+
23
+ it { expect { auth }.to raise_error TypeError }
24
+ end
25
+
26
+ context "when mechanism doesn't exists" do
27
+ let(:auth) { described_class.new("hello@localhost/my-res", "my-password", mechanism: :unknown) }
28
+
29
+ it { expect { auth }.to raise_error ArgumentError }
30
+ end
31
+
32
+ context "when mechanism is digest and no stream_id is given" do
33
+ let(:auth) { described_class.new("hello@localhost/my-res", "my-password", mechanism: :digest) }
34
+
35
+ it { expect { auth }.to raise_error KeyError }
36
+ end
37
+ end
38
+
39
+ describe "#plain" do
40
+ let(:auth) { described_class.new(jid, "my-password") }
41
+ let(:auth_xml) { auth.dump.strip.gsub(/[\r\n]+|\s{2,}/, "") }
42
+ let(:correct_xml) do
43
+ '<iq xmlns="jabber:client" type="set" id="auth2"><query xmlns="jabber:iq:auth">' +
44
+ '<username>strech</username><password>my-password</password>' +
45
+ '<resource>my-resource</resource></query></iq>'
46
+ end
47
+
48
+ before { Jabber.stub(:gen_random_id).and_return "auth2" }
49
+
50
+ it { expect(auth_xml).to eq correct_xml }
51
+ end
52
+
53
+ describe "#digest" do
54
+ let(:auth) { described_class.new(jid, "my-password", stream_id: 1, mechanism: :digest) }
55
+ let(:auth_xml) { auth.dump.strip.gsub(/[\r\n]+|\s{2,}/, "") }
56
+ let(:correct_xml) do
57
+ '<iq xmlns="jabber:client" type="set" id="auth2"><query xmlns="jabber:iq:auth">' +
58
+ '<username>strech</username><digest>0123456789</digest>' +
59
+ '<resource>my-resource</resource></query></iq>'
60
+ end
61
+
62
+ before { Jabber.stub(:gen_random_id).and_return "auth2" }
63
+ before { described_class.stub(:generate_digest).and_return "0123456789" }
64
+
65
+ it { expect(auth_xml).to eq correct_xml }
66
+ end
67
+
68
+ describe "#plain?" do
69
+ let(:auth) { described_class.new(jid, "my-password") }
70
+
71
+ it { expect(auth).to be_plain }
72
+ end
73
+
74
+ describe "#digest?" do
75
+ let(:auth) { described_class.new(jid, "my-password", mechanism: :digest, stream_id: 1) }
76
+
77
+ it { expect(auth).to be_digest }
78
+ end
79
+ end
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Jabber::Bosh::Authentication::SASL::Query do
5
+ let(:jid) { Jabber::JID.new("strech@localhost/my-resource") }
6
+
7
+ describe "#initialize" do
8
+ context "when jid is a String" do
9
+ let(:auth) { described_class.new("hello@localhost/my-res", "my-password") }
10
+
11
+ it { expect { auth }.not_to raise_error TypeError }
12
+ end
13
+
14
+ context "when jid is not instance of Jabber::JID or String" do
15
+ let(:auth) { described_class.new(:hello, "my-password") }
16
+
17
+ it { expect { auth }.to raise_error TypeError }
18
+ end
19
+
20
+ context "when mechanism doesn't exists" do
21
+ let(:auth) { described_class.new("hello@localhost/my-res", "my-password", mechanism: :unknown) }
22
+
23
+ it { expect { auth }.to raise_error ArgumentError }
24
+ end
25
+ end
26
+
27
+ describe "#plain" do
28
+ let(:auth) { described_class.new(jid, "my-password") }
29
+ let(:auth_xml) { auth.dump.strip.gsub(/[\r\n]+|\s{2,}/, "") }
30
+ let(:correct_xml) { '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">0123456789</auth>' }
31
+
32
+ before { described_class.stub(:generate_plain).and_return "0123456789" }
33
+
34
+ it { expect(auth_xml).to eq correct_xml }
35
+ end
36
+
37
+ describe "#plain?" do
38
+ let(:auth) { described_class.new(jid, "my-password") }
39
+
40
+ it { expect(auth).to be_plain }
41
+ end
42
+ end
@@ -0,0 +1,406 @@
1
+ # coding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Jabber::Bosh::Session do
5
+ before { described_class.any_instance.stub(:rand).and_return 1 }
6
+
7
+ describe "#bind" do
8
+ context "when server accept sasl authentication" do
9
+ let(:bosh_session) { described_class.bind("strech@localhost/resource", "password") }
10
+
11
+ let(:successful_login) { '<body xmlns="http://jabber.org/protocol/httpbind"><success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/></body>' }
12
+ let(:successful_open_stream) do
13
+ [
14
+ '<body xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" xmlns:stream="http://etherx.jabber.org/streams"' +
15
+ 'charsets="UTF-8" from="localhost" hold="1" inactivity="20" polling="5" requests="2" sid="67d63e06-4159-43e2-9cb8-544de83eae58"' +
16
+ 'ver="1.6" wait="60" xmpp:version="1.0"><stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism>' +
17
+ '</mechanisms></stream:features></body>'
18
+ ].join " "
19
+ end
20
+ let(:successful_restart) do
21
+ [
22
+ '<body xmlns="http://jabber.org/protocol/httpbind" xmlns:stream="http://etherx.jabber.org/streams">' +
23
+ '<stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/></stream:features></body>'
24
+ ].join " "
25
+ end
26
+ let(:successful_bind) do
27
+ [
28
+ '<body xmlns="http://jabber.org/protocol/httpbind"><iq id="Jabber4R_f7e7fc2a10013070" type="result">' +
29
+ '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>strech@localhost/resource</jid></bind></iq></body>'
30
+ ].join " "
31
+ end
32
+
33
+ let(:is_open_stream?) { ->(data) { x = Ox.parse(data); "2" == x[:rid] } }
34
+ let(:is_login?) { ->(data) { x = Ox.parse(data); "3" == x[:rid] && !x.locate("auth").empty? } }
35
+ let(:is_restart?) { ->(data) { x = Ox.parse(data); "4" == x[:rid] && x["xmpp:restart"] = "true" } }
36
+ let(:is_bind?) { ->(data) { x = Ox.parse(data); "5" == x[:rid] && !x.locate("iq/bind").empty? } }
37
+
38
+ context "when session successfuly binded" do
39
+ before do
40
+ stub_request(:post, "http://localhost:5280/http-bind")
41
+ .with(body: is_open_stream?)
42
+ .to_return(body: successful_open_stream)
43
+
44
+ stub_request(:post, "http://localhost:5280/http-bind")
45
+ .with(body: is_login?)
46
+ .to_return(body: successful_login)
47
+
48
+ stub_request(:post, "http://localhost:5280/http-bind")
49
+ .with(body: is_restart?)
50
+ .to_return(body: successful_restart)
51
+
52
+ stub_request(:post, "http://localhost:5280/http-bind")
53
+ .with(body: is_bind?)
54
+ .to_return(body: successful_bind)
55
+ end
56
+
57
+ it { expect(bosh_session).to be_alive }
58
+ end
59
+
60
+ context "when couldn't open stream" do
61
+ context "when response body missed sid attribute" do
62
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" xmlns:stream="http://etherx.jabber.org/streams"/>' }
63
+
64
+ before do
65
+ stub_request(:post, "http://localhost:5280/http-bind")
66
+ .with(body: is_open_stream?)
67
+ .to_return(body: malformed_body)
68
+
69
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
70
+ .with(body: is_login?)
71
+
72
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
73
+ .with(body: is_restart?)
74
+
75
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
76
+ .with(body: is_bind?)
77
+ end
78
+
79
+ it { expect { bosh_session }.to raise_error Jabber::XMLMalformedError, "Couldn't find <body /> attribute [sid]" }
80
+ end
81
+
82
+ context "when response was not 200 OK" do
83
+ before do
84
+ stub_request(:post, "http://localhost:5280/http-bind")
85
+ .with(body: is_open_stream?)
86
+ .to_return(body: "Foo", status: 401)
87
+
88
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
89
+ .with(body: is_login?)
90
+
91
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
92
+ .with(body: is_restart?)
93
+
94
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
95
+ .with(body: is_bind?)
96
+ end
97
+
98
+ it { expect { bosh_session }.to raise_error Net::HTTPBadResponse }
99
+ end
100
+ end
101
+
102
+ context "when couldn't login in opened stream" do
103
+ context "when response has no auth mechanisms" do
104
+ let(:malformed_body) do
105
+ [
106
+ '<body xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" xmlns:stream="http://etherx.jabber.org/streams"' +
107
+ 'charsets="UTF-8" from="localhost" hold="1" inactivity="20" polling="5" requests="2" sid="67d63e06-4159-43e2-9cb8-544de83eae58"' +
108
+ 'ver="1.6" wait="60" xmpp:version="1.0"/>'
109
+ ].join " "
110
+ end
111
+
112
+ before do
113
+ stub_request(:post, "http://localhost:5280/http-bind")
114
+ .with(body: is_open_stream?)
115
+ .to_return(body: malformed_body)
116
+
117
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
118
+ .with(body: is_login?)
119
+
120
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
121
+ .with(body: is_restart?)
122
+
123
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
124
+ .with(body: is_bind?)
125
+ end
126
+
127
+ it { expect { bosh_session }.to raise_error TypeError, "Server SASL mechanisms not include PLAIN mechanism" }
128
+ end
129
+
130
+ context "when response has type not equal 'success'" do
131
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind"><error xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/></body>' }
132
+
133
+ before do
134
+ stub_request(:post, "http://localhost:5280/http-bind")
135
+ .with(body: is_open_stream?)
136
+ .to_return(body: successful_open_stream)
137
+
138
+ stub_request(:post, "http://localhost:5280/http-bind")
139
+ .with(body: is_login?)
140
+ .to_return(body: malformed_body)
141
+
142
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
143
+ .with(body: is_restart?)
144
+
145
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
146
+ .with(body: is_bind?)
147
+ end
148
+
149
+ it { expect { bosh_session }.to raise_error Jabber::AuthenticationError, "Failed to login" }
150
+ end
151
+
152
+ context "when response was not 200 OK" do
153
+ before do
154
+ stub_request(:post, "http://localhost:5280/http-bind")
155
+ .with(body: is_open_stream?)
156
+ .to_return(body: successful_open_stream)
157
+
158
+ stub_request(:post, "http://localhost:5280/http-bind")
159
+ .with(body: is_login?)
160
+ .to_return(body: "Foo", status: 401)
161
+
162
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
163
+ .with(body: is_restart?)
164
+
165
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
166
+ .with(body: is_bind?)
167
+ end
168
+
169
+ it { expect { bosh_session }.to raise_error Net::HTTPBadResponse }
170
+ end
171
+ end
172
+
173
+ context "when cann't restart opened stream" do
174
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind" xmlns:stream="http://etherx.jabber.org/streams"><error xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/></body>' }
175
+
176
+ before do
177
+ stub_request(:post, "http://localhost:5280/http-bind")
178
+ .with(body: is_open_stream?)
179
+ .to_return(body: successful_open_stream)
180
+
181
+ stub_request(:post, "http://localhost:5280/http-bind")
182
+ .with(body: is_login?)
183
+ .to_return(body: successful_login)
184
+
185
+ stub_request(:post, "http://localhost:5280/http-bind")
186
+ .with(body: is_restart?)
187
+ .to_return(body: malformed_body)
188
+
189
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
190
+ .with(body: is_bind?)
191
+ end
192
+
193
+ it { expect { bosh_session }.to raise_error Jabber::AuthenticationError, "Failed to login" }
194
+ end
195
+
196
+ context "when cann't bind resource on opened stream" do
197
+ context "when response is a malformed xml without IQ tag" do
198
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind"></body>' }
199
+
200
+ before do
201
+ stub_request(:post, "http://localhost:5280/http-bind")
202
+ .with(body: is_open_stream?)
203
+ .to_return(body: successful_open_stream)
204
+
205
+ stub_request(:post, "http://localhost:5280/http-bind")
206
+ .with(body: is_login?)
207
+ .to_return(body: successful_login)
208
+
209
+ stub_request(:post, "http://localhost:5280/http-bind")
210
+ .with(body: is_restart?)
211
+ .to_return(body: successful_restart)
212
+
213
+ stub_request(:post, "http://localhost:5280/http-bind")
214
+ .with(body: is_bind?)
215
+ .to_return(body: malformed_body)
216
+ end
217
+
218
+ it { expect { bosh_session }.to raise_error Jabber::XMLMalformedError, "Couldn't find xml tag <iq/>" }
219
+ end
220
+
221
+ context "when response has type not equal 'result'" do
222
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind"><iq id="Jabber4R_f7e7fc2a10013070" type="error"/></body>' }
223
+
224
+ before do
225
+ stub_request(:post, "http://localhost:5280/http-bind")
226
+ .with(body: is_open_stream?)
227
+ .to_return(body: successful_open_stream)
228
+
229
+ stub_request(:post, "http://localhost:5280/http-bind")
230
+ .with(body: is_login?)
231
+ .to_return(body: successful_login)
232
+
233
+ stub_request(:post, "http://localhost:5280/http-bind")
234
+ .with(body: is_restart?)
235
+ .to_return(body: successful_restart)
236
+
237
+ stub_request(:post, "http://localhost:5280/http-bind")
238
+ .with(body: is_bind?)
239
+ .to_return(body: malformed_body)
240
+ end
241
+
242
+ it { expect { bosh_session }.to raise_error Jabber::AuthenticationError, "Failed to login" }
243
+ end
244
+ end
245
+ end
246
+
247
+ # REFACTOR ALL
248
+
249
+ context "when server accept non-sasl authentication" do
250
+ let(:bosh_session) { described_class.bind("strech@localhost/resource", "password", use_sasl: false) }
251
+
252
+ let(:successful_login) { '<body xmlns="http://jabber.org/protocol/httpbind"><iq type="result" xmlns="jabber:client" id="2034"/></body>' }
253
+ let(:successful_open_stream) do
254
+ [
255
+ '<body xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" xmlns:stream="http://etherx.jabber.org/streams"',
256
+ 'authid="3780309894" sid="ce21410" from="localhost" xmpp:version="1.0"',
257
+ 'wait="60" requests="2" inactivity="30" maxpause="120" polling="2" ver="1.8" secure="true" />'
258
+ ].join " "
259
+ end
260
+ let(:is_open_stream?) { ->(data) { x = Ox.parse(data); ["2", "localhost"] == [x[:rid], x[:to]] } }
261
+ let(:is_login?) { ->(data) { x = Ox.parse(data); ["3", "ce21410"] == [x[:rid], x[:sid]] } }
262
+
263
+ context "when session successfuly binded" do
264
+ before do
265
+ stub_request(:post, "http://localhost:5280/http-bind")
266
+ .with(body: is_open_stream?)
267
+ .to_return(body: successful_open_stream)
268
+
269
+ stub_request(:post, "http://localhost:5280/http-bind")
270
+ .with(body: is_login?)
271
+ .to_return(body: successful_login)
272
+ end
273
+
274
+ it { expect(bosh_session).to be_alive }
275
+ end
276
+
277
+ context "when couldn't open stream" do
278
+ context "when response body missed authid attribute" do
279
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind" sid="ce21410" />' }
280
+
281
+ before do
282
+ stub_request(:post, "http://localhost:5280/http-bind")
283
+ .with(body: is_open_stream?)
284
+ .to_return(body: malformed_body)
285
+
286
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
287
+ .with(body: is_login?)
288
+ end
289
+
290
+ it { expect { bosh_session }.to raise_error Jabber::XMLMalformedError, "Couldn't find <body /> attribute [authid]" }
291
+ end
292
+
293
+ context "when response body missed sid attribute" do
294
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind" authid="3780309894" />' }
295
+
296
+ before do
297
+ stub_request(:post, "http://localhost:5280/http-bind")
298
+ .with(body: is_open_stream?)
299
+ .to_return(body: malformed_body)
300
+
301
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
302
+ .with(body: is_login?)
303
+ end
304
+
305
+ it { expect { bosh_session }.to raise_error Jabber::XMLMalformedError, "Couldn't find <body /> attribute [sid]" }
306
+ end
307
+
308
+ context "when response was not 200 OK" do
309
+ before do
310
+ stub_request(:post, "http://localhost:5280/http-bind")
311
+ .with(body: is_open_stream?)
312
+ .to_return(body: "Foo", status: 401)
313
+
314
+ WebMock.should_not have_requested(:post, "http://localhost:5280/http-bind")
315
+ .with(body: is_login?)
316
+ end
317
+
318
+ it { expect { bosh_session }.to raise_error Net::HTTPBadResponse }
319
+ end
320
+ end
321
+
322
+ context "when couldn't login in opened stream" do
323
+ context "when response is a malformed xml without IQ tag" do
324
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind"></body>' }
325
+
326
+ before do
327
+ stub_request(:post, "http://localhost:5280/http-bind")
328
+ .with(body: is_open_stream?)
329
+ .to_return(body: successful_open_stream)
330
+
331
+ stub_request(:post, "http://localhost:5280/http-bind")
332
+ .with(body: is_login?)
333
+ .to_return(body: malformed_body)
334
+ end
335
+
336
+ it { expect { bosh_session }.to raise_error Jabber::XMLMalformedError, "Couldn't find xml tag <iq/>" }
337
+ end
338
+
339
+ context "when response has type not equal 'result'" do
340
+ let(:malformed_body) { '<body xmlns="http://jabber.org/protocol/httpbind"><iq type="error" xmlns="jabber:client" id="2034"/></body>' }
341
+
342
+ before do
343
+ stub_request(:post, "http://localhost:5280/http-bind")
344
+ .with(body: is_open_stream?)
345
+ .to_return(body: successful_open_stream)
346
+
347
+ stub_request(:post, "http://localhost:5280/http-bind")
348
+ .with(body: is_login?)
349
+ .to_return(body: malformed_body)
350
+ end
351
+
352
+ it { expect { bosh_session }.to raise_error Jabber::AuthenticationError, "Failed to login" }
353
+ end
354
+
355
+ context "when response was not 200 OK" do
356
+ before do
357
+ stub_request(:post, "http://localhost:5280/http-bind")
358
+ .with(body: is_open_stream?)
359
+ .to_return(body: successful_open_stream)
360
+
361
+ stub_request(:post, "http://localhost:5280/http-bind")
362
+ .with(body: is_login?)
363
+ .to_return(body: "Foo", status: 401)
364
+ end
365
+
366
+ it { expect { bosh_session }.to raise_error Net::HTTPBadResponse }
367
+ end
368
+ end
369
+ end
370
+ end
371
+
372
+ describe "#to_json" do
373
+ let(:bosh_session) { described_class.bind("strech@localhost/resource", "password", use_sasl: false) }
374
+ let(:successful_login) { '<body xmlns="http://jabber.org/protocol/httpbind"><iq type="result" xmlns="jabber:client" id="2034"/></body>' }
375
+ let(:successful_open_stream) do
376
+ [
377
+ '<body xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" xmlns:stream="http://etherx.jabber.org/streams"',
378
+ 'authid="3780309894" sid="ce21410" from="localhost" xmpp:version="1.0"',
379
+ 'wait="60" requests="2" inactivity="30" maxpause="120" polling="2" ver="1.8" secure="true" />'
380
+ ].join " "
381
+ end
382
+ let(:is_open_stream?) { ->(data) { x = Ox.parse(data); ["2", "localhost"] == [x[:rid], x[:to]] } }
383
+ let(:is_login?) { ->(data) { x = Ox.parse(data); ["3", "ce21410"] == [x[:rid], x[:sid]] } }
384
+
385
+ let(:json) { JSON.parse(bosh_session.to_json) }
386
+
387
+ before do
388
+ stub_request(:post, "http://localhost:5280/http-bind")
389
+ .with(body: is_open_stream?)
390
+ .to_return(body: successful_open_stream)
391
+
392
+
393
+ stub_request(:post, "http://localhost:5280/http-bind")
394
+ .with(body: is_login?)
395
+ .to_return(body: successful_login)
396
+ end
397
+
398
+ it { expect(json).to have_key "jid" }
399
+ it { expect(json).to have_key "rid" }
400
+ it { expect(json).to have_key "sid" }
401
+
402
+ it { expect(json["jid"]).to eq "strech@localhost/resource"}
403
+ it { expect(json["rid"]).to eq 3}
404
+ it { expect(json["sid"]).to eq "ce21410"}
405
+ end
406
+ end