pusher 2.0.0 → 2.0.1

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.
@@ -1,7 +0,0 @@
1
- ## Description
2
-
3
- Add a short description of the change. If this is related to an issue, please add a reference to the issue.
4
-
5
- ## CHANGELOG
6
-
7
- * [CHANGED] Describe your change here. Look at CHANGELOG.md to see the format.
data/pusher.gemspec DELETED
@@ -1,36 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- require File.expand_path('../lib/pusher/version', __FILE__)
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "pusher"
7
- s.version = Pusher::VERSION
8
- s.platform = Gem::Platform::RUBY
9
- s.authors = ["Pusher"]
10
- s.email = ["support@pusher.com"]
11
- s.homepage = "http://github.com/pusher/pusher-http-ruby"
12
- s.summary = %q{Pusher Channels API client}
13
- s.description = %q{Wrapper for Pusher Channels REST api: : https://pusher.com/channels}
14
- s.license = "MIT"
15
-
16
- s.required_ruby_version = ">= 2.6"
17
-
18
- s.add_dependency "multi_json", "~> 1.15"
19
- s.add_dependency 'pusher-signature', "~> 0.1.8"
20
- s.add_dependency "httpclient", "~> 2.8"
21
- s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
22
-
23
- s.add_development_dependency "rspec", "~> 3.9"
24
- s.add_development_dependency "webmock", "~> 3.9"
25
- s.add_development_dependency "em-http-request", "~> 1.1"
26
- s.add_development_dependency "addressable", "~> 2.7"
27
- s.add_development_dependency "rake", "~> 13.0"
28
- s.add_development_dependency "rack", "~> 2.2"
29
- s.add_development_dependency "json", "~> 2.3"
30
- s.add_development_dependency "rbnacl", "~> 7.1"
31
-
32
- s.files = `git ls-files`.split("\n")
33
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
34
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
35
- s.require_paths = ["lib"]
36
- end
data/spec/channel_spec.rb DELETED
@@ -1,187 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require 'spec_helper'
3
-
4
- describe Pusher::Channel do
5
- before do
6
- @client = Pusher::Client.new({
7
- :app_id => '20',
8
- :key => '12345678900000001',
9
- :secret => '12345678900000001',
10
- :host => 'api.pusherapp.com',
11
- :port => 80,
12
- })
13
- @channel = @client['test_channel']
14
- end
15
-
16
- let(:pusher_url_regexp) { %r{/apps/20/events} }
17
-
18
- def stub_post(status, body = nil)
19
- options = {:status => status}
20
- options.merge!({:body => body}) if body
21
-
22
- stub_request(:post, pusher_url_regexp).to_return(options)
23
- end
24
-
25
- def stub_post_to_raise(e)
26
- stub_request(:post, pusher_url_regexp).to_raise(e)
27
- end
28
-
29
- describe '#trigger!' do
30
- it "should use @client.trigger internally" do
31
- expect(@client).to receive(:trigger)
32
- @channel.trigger!('new_event', 'Some data')
33
- end
34
- end
35
-
36
- describe '#trigger' do
37
- it "should log failure if error raised in http call" do
38
- stub_post_to_raise(HTTPClient::BadResponseError)
39
-
40
- expect(Pusher.logger).to receive(:error).with("Exception from WebMock (HTTPClient::BadResponseError) (Pusher::HTTPError)")
41
- expect(Pusher.logger).to receive(:debug) #backtrace
42
- @channel.trigger('new_event', 'Some data')
43
- end
44
-
45
- it "should log failure if Pusher returns an error response" do
46
- stub_post 401, "some signature info"
47
- expect(Pusher.logger).to receive(:error).with("some signature info (Pusher::AuthenticationError)")
48
- expect(Pusher.logger).to receive(:debug) #backtrace
49
- @channel.trigger('new_event', 'Some data')
50
- end
51
- end
52
-
53
- describe "#initialization" do
54
- it "should not be too long" do
55
- expect { @client['b'*201] }.to raise_error(Pusher::Error)
56
- end
57
-
58
- it "should not use bad characters" do
59
- expect { @client['*^!±`/""'] }.to raise_error(Pusher::Error)
60
- end
61
- end
62
-
63
- describe "#trigger_async" do
64
- it "should use @client.trigger_async internally" do
65
- expect(@client).to receive(:trigger_async)
66
- @channel.trigger_async('new_event', 'Some data')
67
- end
68
- end
69
-
70
- describe '#info' do
71
- it "should call the Client#channel_info" do
72
- expect(@client).to receive(:get)
73
- .with("/channels/mychannel", anything)
74
- .and_return({:occupied => true, :subscription_count => 12})
75
- @channel = @client['mychannel']
76
- @channel.info
77
- end
78
-
79
- it "should assemble the requested attributes into the info option" do
80
- expect(@client).to receive(:get)
81
- .with(anything, {:info => "user_count,connection_count"})
82
- .and_return({:occupied => true, :subscription_count => 12, :user_count => 12})
83
- @channel = @client['presence-foo']
84
- @channel.info(%w{user_count connection_count})
85
- end
86
- end
87
-
88
- describe '#users' do
89
- it "should call the Client#channel_users" do
90
- expect(@client).to receive(:get).with("/channels/presence-mychannel/users", {}).and_return({:users => {'id' => '4'}})
91
- @channel = @client['presence-mychannel']
92
- @channel.users
93
- end
94
- end
95
-
96
- describe "#authentication_string" do
97
- def authentication_string(*data)
98
- lambda { @channel.authentication_string(*data) }
99
- end
100
-
101
- it "should return an authentication string given a socket id" do
102
- auth = @channel.authentication_string('1.1')
103
-
104
- expect(auth).to eq('12345678900000001:02259dff9a2a3f71ea8ab29ac0c0c0ef7996c8f3fd3702be5533f30da7d7fed4')
105
- end
106
-
107
- it "should raise error if authentication is invalid" do
108
- [nil, ''].each do |invalid|
109
- expect(authentication_string(invalid)).to raise_error Pusher::Error
110
- end
111
- end
112
-
113
- describe 'with extra string argument' do
114
- it 'should be a string or nil' do
115
- expect(authentication_string('1.1', 123)).to raise_error Pusher::Error
116
- expect(authentication_string('1.1', {})).to raise_error Pusher::Error
117
-
118
- expect(authentication_string('1.1', 'boom')).not_to raise_error
119
- expect(authentication_string('1.1', nil)).not_to raise_error
120
- end
121
-
122
- it "should return an authentication string given a socket id and custom args" do
123
- auth = @channel.authentication_string('1.1', 'foobar')
124
-
125
- expect(auth).to eq("12345678900000001:#{hmac(@client.secret, "1.1:test_channel:foobar")}")
126
- end
127
- end
128
- end
129
-
130
- describe '#authenticate' do
131
- before :each do
132
- @custom_data = {:uid => 123, :info => {:name => 'Foo'}}
133
- end
134
-
135
- it 'should return a hash with signature including custom data and data as json string' do
136
- allow(MultiJson).to receive(:encode).with(@custom_data).and_return 'a json string'
137
-
138
- response = @channel.authenticate('1.1', @custom_data)
139
-
140
- expect(response).to eq({
141
- :auth => "12345678900000001:#{hmac(@client.secret, "1.1:test_channel:a json string")}",
142
- :channel_data => 'a json string'
143
- })
144
- end
145
-
146
- it 'should fail on invalid socket_ids' do
147
- expect {
148
- @channel.authenticate('1.1:')
149
- }.to raise_error Pusher::Error
150
-
151
- expect {
152
- @channel.authenticate('1.1foo', 'channel')
153
- }.to raise_error Pusher::Error
154
-
155
- expect {
156
- @channel.authenticate(':1.1')
157
- }.to raise_error Pusher::Error
158
-
159
- expect {
160
- @channel.authenticate('foo1.1', 'channel')
161
- }.to raise_error Pusher::Error
162
-
163
- expect {
164
- @channel.authenticate('foo', 'channel')
165
- }.to raise_error Pusher::Error
166
- end
167
- end
168
-
169
- describe `#shared_secret` do
170
- before(:each) do
171
- @channel.instance_variable_set(:@name, 'private-encrypted-1')
172
- end
173
-
174
- it 'should return a shared_secret based on the channel name and encryption master key' do
175
- key = '3W1pfB/Etr+ZIlfMWwZP3gz8jEeCt4s2pe6Vpr+2c3M='
176
- shared_secret = @channel.shared_secret(key)
177
- expect(Base64.strict_encode64(shared_secret)).to eq(
178
- "6zeEp/chneRPS1cbK/hGeG860UhHomxSN6hTgzwT20I="
179
- )
180
- end
181
-
182
- it 'should return nil if missing encryption master key' do
183
- shared_secret = @channel.shared_secret(nil)
184
- expect(shared_secret).to be_nil
185
- end
186
- end
187
- end
data/spec/client_spec.rb DELETED
@@ -1,741 +0,0 @@
1
- require 'base64'
2
-
3
- require 'rbnacl'
4
- require 'em-http'
5
-
6
- require 'spec_helper'
7
-
8
- encryption_master_key = RbNaCl::Random.random_bytes(32)
9
-
10
- describe Pusher do
11
- # The behaviour should be the same when using the Client object, or the
12
- # 'global' client delegated through the Pusher class
13
- [lambda { Pusher }, lambda { Pusher::Client.new }].each do |client_gen|
14
- before :each do
15
- @client = client_gen.call
16
- end
17
-
18
- describe 'default configuration' do
19
- it 'should be preconfigured for api host' do
20
- expect(@client.host).to eq('api-mt1.pusher.com')
21
- end
22
-
23
- it 'should be preconfigured for port 443' do
24
- expect(@client.port).to eq(443)
25
- end
26
-
27
- it 'should use standard logger if no other logger if defined' do
28
- Pusher.logger.debug('foo')
29
- expect(Pusher.logger).to be_kind_of(Logger)
30
- end
31
- end
32
-
33
- describe 'logging configuration' do
34
- it "can be configured to use any logger" do
35
- logger = double("ALogger")
36
- expect(logger).to receive(:debug).with('foo')
37
- Pusher.logger = logger
38
- Pusher.logger.debug('foo')
39
- Pusher.logger = nil
40
- end
41
- end
42
-
43
- describe "configuration using url" do
44
- it "should be possible to configure everything by setting the url" do
45
- @client.url = "test://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
46
-
47
- expect(@client.scheme).to eq('test')
48
- expect(@client.host).to eq('api.staging.pusherapp.com')
49
- expect(@client.port).to eq(8080)
50
- expect(@client.key).to eq('somekey')
51
- expect(@client.secret).to eq('somesecret')
52
- expect(@client.app_id).to eq('87')
53
- end
54
-
55
- it "should override scheme and port when setting encrypted=true after url" do
56
- @client.url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
57
- @client.encrypted = true
58
-
59
- expect(@client.scheme).to eq('https')
60
- expect(@client.port).to eq(443)
61
- end
62
-
63
- it "should fail on bad urls" do
64
- expect { @client.url = "gopher/somekey:somesecret@://api.staging.pusherapp.co://m:8080\apps\87" }.to raise_error(URI::InvalidURIError)
65
- end
66
-
67
- it "should raise exception if app_id is not configured" do
68
- @client.app_id = nil
69
- expect {
70
- @client.url
71
- }.to raise_error(Pusher::ConfigurationError)
72
- end
73
-
74
- end
75
-
76
- describe 'configuring the cluster' do
77
- it 'should set a new default host' do
78
- @client.cluster = 'eu'
79
- expect(@client.host).to eq('api-eu.pusher.com')
80
- end
81
-
82
- it 'should handle nil gracefully' do
83
- @client.cluster = nil
84
- expect(@client.host).to eq('api-mt1.pusher.com')
85
- end
86
-
87
- it 'should handle empty string' do
88
- @client.cluster = ""
89
- expect(@client.host).to eq('api-mt1.pusher.com')
90
- end
91
-
92
- it 'should be overridden by host if it comes after' do
93
- @client.cluster = 'eu'
94
- @client.host = 'api.staging.pusher.com'
95
- expect(@client.host).to eq('api.staging.pusher.com')
96
- end
97
-
98
- it 'should be overridden by url if it comes after' do
99
- @client.cluster = 'eu'
100
- @client.url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
101
-
102
- expect(@client.host).to eq('api.staging.pusherapp.com')
103
- end
104
-
105
- it 'should override the url configuration if it comes after' do
106
- @client.url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
107
- @client.cluster = 'eu'
108
- expect(@client.host).to eq('api-eu.pusher.com')
109
- end
110
-
111
- it 'should override the host configuration if it comes after' do
112
- @client.host = 'api.staging.pusher.com'
113
- @client.cluster = 'eu'
114
- expect(@client.host).to eq('api-eu.pusher.com')
115
- end
116
- end
117
-
118
- describe 'configuring TLS' do
119
- it 'should set port and scheme if "use_tls" disabled' do
120
- client = Pusher::Client.new({
121
- :use_tls => false,
122
- })
123
- expect(client.scheme).to eq('http')
124
- expect(client.port).to eq(80)
125
- end
126
-
127
- it 'should set port and scheme if "encrypted" disabled' do
128
- client = Pusher::Client.new({
129
- :encrypted => false,
130
- })
131
- expect(client.scheme).to eq('http')
132
- expect(client.port).to eq(80)
133
- end
134
-
135
- it 'should use TLS port and scheme if "encrypted" or "use_tls" are not set' do
136
- client = Pusher::Client.new
137
- expect(client.scheme).to eq('https')
138
- expect(client.port).to eq(443)
139
- end
140
-
141
- it 'should override port if "use_tls" option set but a different port is specified' do
142
- client = Pusher::Client.new({
143
- :use_tls => true,
144
- :port => 8443
145
- })
146
- expect(client.scheme).to eq('https')
147
- expect(client.port).to eq(8443)
148
- end
149
-
150
- it 'should override port if "use_tls" option set but a different port is specified' do
151
- client = Pusher::Client.new({
152
- :use_tls => false,
153
- :port => 8000
154
- })
155
- expect(client.scheme).to eq('http')
156
- expect(client.port).to eq(8000)
157
- end
158
-
159
- end
160
-
161
- describe 'configuring a http proxy' do
162
- it "should be possible to configure everything by setting the http_proxy" do
163
- @client.http_proxy = 'http://someuser:somepassword@proxy.host.com:8080'
164
-
165
- expect(@client.proxy).to eq({:scheme => 'http', :host => 'proxy.host.com', :port => 8080, :user => 'someuser', :password => 'somepassword'})
166
- end
167
- end
168
-
169
- describe 'configuring from env' do
170
- after do
171
- ENV['PUSHER_URL'] = nil
172
- end
173
-
174
- it "works" do
175
- url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
176
- ENV['PUSHER_URL'] = url
177
-
178
- client = Pusher::Client.from_env
179
- expect(client.key).to eq("somekey")
180
- expect(client.secret).to eq("somesecret")
181
- expect(client.app_id).to eq("87")
182
- expect(client.url.to_s).to eq("http://api.staging.pusherapp.com:8080/apps/87")
183
- end
184
- end
185
-
186
- describe 'configuring from url' do
187
- it "works" do
188
- url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
189
-
190
- client = Pusher::Client.from_url(url)
191
- expect(client.key).to eq("somekey")
192
- expect(client.secret).to eq("somesecret")
193
- expect(client.app_id).to eq("87")
194
- expect(client.url.to_s).to eq("http://api.staging.pusherapp.com:8080/apps/87")
195
- end
196
- end
197
-
198
- describe 'can set encryption_master_key_base64' do
199
- it "sets encryption_master_key" do
200
- @client.encryption_master_key_base64 =
201
- Base64.strict_encode64(encryption_master_key)
202
-
203
- expect(@client.encryption_master_key).to eq(encryption_master_key)
204
- end
205
- end
206
-
207
- describe 'when configured' do
208
- before :each do
209
- @client.app_id = '20'
210
- @client.key = '12345678900000001'
211
- @client.secret = '12345678900000001'
212
- @client.encryption_master_key_base64 =
213
- Base64.strict_encode64(encryption_master_key)
214
- end
215
-
216
- describe '#[]' do
217
- before do
218
- @channel = @client['test_channel']
219
- end
220
-
221
- it 'should return a channel' do
222
- expect(@channel).to be_kind_of(Pusher::Channel)
223
- end
224
-
225
- it "should raise exception if app_id is not configured" do
226
- @client.app_id = nil
227
- expect {
228
- @channel.trigger!('foo', 'bar')
229
- }.to raise_error(Pusher::ConfigurationError)
230
- end
231
- end
232
-
233
- describe '#channels' do
234
- it "should call the correct URL and symbolise response correctly" do
235
- api_path = %r{/apps/20/channels}
236
- stub_request(:get, api_path).to_return({
237
- :status => 200,
238
- :body => MultiJson.encode('channels' => {
239
- "channel1" => {},
240
- "channel2" => {}
241
- })
242
- })
243
- expect(@client.channels).to eq({
244
- :channels => {
245
- "channel1" => {},
246
- "channel2" => {}
247
- }
248
- })
249
- end
250
- end
251
-
252
- describe '#channel_info' do
253
- it "should call correct URL and symbolise response" do
254
- api_path = %r{/apps/20/channels/mychannel}
255
- stub_request(:get, api_path).to_return({
256
- :status => 200,
257
- :body => MultiJson.encode({
258
- 'occupied' => false,
259
- })
260
- })
261
- expect(@client.channel_info('mychannel')).to eq({
262
- :occupied => false,
263
- })
264
- end
265
- end
266
-
267
- describe '#channel_users' do
268
- it "should call correct URL and symbolise response" do
269
- api_path = %r{/apps/20/channels/mychannel/users}
270
- stub_request(:get, api_path).to_return({
271
- :status => 200,
272
- :body => MultiJson.encode({
273
- 'users' => [{ 'id' => 1 }]
274
- })
275
- })
276
- expect(@client.channel_users('mychannel')).to eq({
277
- :users => [{ 'id' => 1}]
278
- })
279
- end
280
- end
281
-
282
- describe '#authenticate' do
283
- before :each do
284
- @custom_data = {:uid => 123, :info => {:name => 'Foo'}}
285
- end
286
-
287
- it 'should return a hash with signature including custom data and data as json string' do
288
- allow(MultiJson).to receive(:encode).with(@custom_data).and_return 'a json string'
289
-
290
- response = @client.authenticate('test_channel', '1.1', @custom_data)
291
-
292
- expect(response).to eq({
293
- :auth => "12345678900000001:#{hmac(@client.secret, "1.1:test_channel:a json string")}",
294
- :channel_data => 'a json string'
295
- })
296
- end
297
-
298
- it 'should include a shared_secret if the private-encrypted channel' do
299
- allow(MultiJson).to receive(:encode).with(@custom_data).and_return 'a json string'
300
- @client.instance_variable_set(:@encryption_master_key, '3W1pfB/Etr+ZIlfMWwZP3gz8jEeCt4s2pe6Vpr+2c3M=')
301
-
302
- response = @client.authenticate('private-encrypted-test_channel', '1.1', @custom_data)
303
-
304
- expect(response).to eq({
305
- :auth => "12345678900000001:#{hmac(@client.secret, "1.1:private-encrypted-test_channel:a json string")}",
306
- :shared_secret => "o0L3QnIovCeRC8KTD8KBRlmi31dGzHVS2M93uryqDdw=",
307
- :channel_data => 'a json string'
308
- })
309
- end
310
-
311
- end
312
-
313
- describe '#trigger' do
314
- before :each do
315
- @api_path = %r{/apps/20/events}
316
- stub_request(:post, @api_path).to_return({
317
- :status => 200,
318
- :body => MultiJson.encode({})
319
- })
320
- end
321
-
322
- it "should call correct URL" do
323
- expect(@client.trigger(['mychannel'], 'event', {'some' => 'data'})).
324
- to eq({})
325
- end
326
-
327
- it "should not allow too many channels" do
328
- expect {
329
- @client.trigger((0..11).map{|i| 'mychannel#{i}'},
330
- 'event', {'some' => 'data'}, {
331
- :socket_id => "12.34"
332
- })}.to raise_error(Pusher::Error)
333
- end
334
-
335
- it "should pass any parameters in the body of the request" do
336
- @client.trigger(['mychannel', 'c2'], 'event', {'some' => 'data'}, {
337
- :socket_id => "12.34"
338
- })
339
- expect(WebMock).to have_requested(:post, @api_path).with { |req|
340
- parsed = MultiJson.decode(req.body)
341
- expect(parsed["name"]).to eq('event')
342
- expect(parsed["channels"]).to eq(["mychannel", "c2"])
343
- expect(parsed["socket_id"]).to eq('12.34')
344
- }
345
- end
346
-
347
- it "should convert non string data to JSON before posting" do
348
- @client.trigger(['mychannel'], 'event', {'some' => 'data'})
349
- expect(WebMock).to have_requested(:post, @api_path).with { |req|
350
- expect(MultiJson.decode(req.body)["data"]).to eq('{"some":"data"}')
351
- }
352
- end
353
-
354
- it "should accept a single channel as well as an array" do
355
- @client.trigger('mychannel', 'event', {'some' => 'data'})
356
- expect(WebMock).to have_requested(:post, @api_path).with { |req|
357
- expect(MultiJson.decode(req.body)["channels"]).to eq(['mychannel'])
358
- }
359
- end
360
-
361
- %w[app_id key secret].each do |key|
362
- it "should fail in missing #{key}" do
363
- @client.public_send("#{key}=", nil)
364
- expect {
365
- @client.trigger('mychannel', 'event', {'some' => 'data'})
366
- }.to raise_error(Pusher::ConfigurationError)
367
- expect(WebMock).not_to have_requested(:post, @api_path).with { |req|
368
- expect(MultiJson.decode(req.body)["channels"]).to eq(['mychannel'])
369
- }
370
- end
371
- end
372
-
373
- it "should fail to publish to encrypted channels when missing key" do
374
- @client.encryption_master_key_base64 = nil
375
- expect {
376
- @client.trigger('private-encrypted-channel', 'event', {'some' => 'data'})
377
- }.to raise_error(Pusher::ConfigurationError)
378
- expect(WebMock).not_to have_requested(:post, @api_path)
379
- end
380
-
381
- it "should fail to publish to multiple channels if one is encrypted" do
382
- expect {
383
- @client.trigger(
384
- ['private-encrypted-channel', 'some-other-channel'],
385
- 'event',
386
- {'some' => 'data'},
387
- )
388
- }.to raise_error(Pusher::Error)
389
- expect(WebMock).not_to have_requested(:post, @api_path)
390
- end
391
-
392
- it "should encrypt publishes to encrypted channels" do
393
- @client.trigger(
394
- 'private-encrypted-channel',
395
- 'event',
396
- {'some' => 'data'},
397
- )
398
-
399
- expect(WebMock).to have_requested(:post, @api_path).with { |req|
400
- data = MultiJson.decode(MultiJson.decode(req.body)["data"])
401
-
402
- key = RbNaCl::Hash.sha256(
403
- 'private-encrypted-channel' + encryption_master_key
404
- )
405
-
406
- expect(MultiJson.decode(RbNaCl::SecretBox.new(key).decrypt(
407
- Base64.strict_decode64(data["nonce"]),
408
- Base64.strict_decode64(data["ciphertext"]),
409
- ))).to eq({ 'some' => 'data' })
410
- }
411
- end
412
- end
413
-
414
- describe '#trigger_batch' do
415
- before :each do
416
- @api_path = %r{/apps/20/batch_events}
417
- stub_request(:post, @api_path).to_return({
418
- :status => 200,
419
- :body => MultiJson.encode({})
420
- })
421
- end
422
-
423
- it "should call correct URL" do
424
- expect(@client.trigger_batch(channel: 'mychannel', name: 'event', data: {'some' => 'data'})).
425
- to eq({})
426
- end
427
-
428
- it "should convert non string data to JSON before posting" do
429
- @client.trigger_batch(
430
- {channel: 'mychannel', name: 'event', data: {'some' => 'data'}},
431
- {channel: 'mychannel', name: 'event', data: 'already encoded'},
432
- )
433
- expect(WebMock).to have_requested(:post, @api_path).with { |req|
434
- parsed = MultiJson.decode(req.body)
435
- expect(parsed).to eq(
436
- "batch" => [
437
- { "channel" => "mychannel", "name" => "event", "data" => "{\"some\":\"data\"}"},
438
- { "channel" => "mychannel", "name" => "event", "data" => "already encoded"}
439
- ]
440
- )
441
- }
442
- end
443
-
444
- it "should fail to publish to encrypted channels when missing key" do
445
- @client.encryption_master_key_base64 = nil
446
- expect {
447
- @client.trigger_batch(
448
- {
449
- channel: 'private-encrypted-channel',
450
- name: 'event',
451
- data: {'some' => 'data'},
452
- },
453
- {channel: 'mychannel', name: 'event', data: 'already encoded'},
454
- )
455
- }.to raise_error(Pusher::ConfigurationError)
456
- expect(WebMock).not_to have_requested(:post, @api_path)
457
- end
458
-
459
- it "should encrypt publishes to encrypted channels" do
460
- @client.trigger_batch(
461
- {
462
- channel: 'private-encrypted-channel',
463
- name: 'event',
464
- data: {'some' => 'data'},
465
- },
466
- {channel: 'mychannel', name: 'event', data: 'already encoded'},
467
- )
468
-
469
- expect(WebMock).to have_requested(:post, @api_path).with { |req|
470
- batch = MultiJson.decode(req.body)["batch"]
471
- expect(batch.length).to eq(2)
472
-
473
- expect(batch[0]["channel"]).to eq("private-encrypted-channel")
474
- expect(batch[0]["name"]).to eq("event")
475
-
476
- data = MultiJson.decode(batch[0]["data"])
477
-
478
- key = RbNaCl::Hash.sha256(
479
- 'private-encrypted-channel' + encryption_master_key
480
- )
481
-
482
- expect(MultiJson.decode(RbNaCl::SecretBox.new(key).decrypt(
483
- Base64.strict_decode64(data["nonce"]),
484
- Base64.strict_decode64(data["ciphertext"]),
485
- ))).to eq({ 'some' => 'data' })
486
-
487
- expect(batch[1]["channel"]).to eq("mychannel")
488
- expect(batch[1]["name"]).to eq("event")
489
- expect(batch[1]["data"]).to eq("already encoded")
490
- }
491
- end
492
- end
493
-
494
- describe '#trigger_async' do
495
- before :each do
496
- @api_path = %r{/apps/20/events}
497
- stub_request(:post, @api_path).to_return({
498
- :status => 200,
499
- :body => MultiJson.encode({})
500
- })
501
- end
502
-
503
- it "should call correct URL" do
504
- EM.run {
505
- @client.trigger_async('mychannel', 'event', {'some' => 'data'}).callback { |r|
506
- expect(r).to eq({})
507
- EM.stop
508
- }
509
- }
510
- end
511
-
512
- it "should pass any parameters in the body of the request" do
513
- EM.run {
514
- @client.trigger_async('mychannel', 'event', {'some' => 'data'}, {
515
- :socket_id => "12.34"
516
- }).callback {
517
- expect(WebMock).to have_requested(:post, @api_path).with { |req|
518
- expect(MultiJson.decode(req.body)["socket_id"]).to eq('12.34')
519
- }
520
- EM.stop
521
- }
522
- }
523
- end
524
-
525
- it "should convert non string data to JSON before posting" do
526
- EM.run {
527
- @client.trigger_async('mychannel', 'event', {'some' => 'data'}).callback {
528
- expect(WebMock).to have_requested(:post, @api_path).with { |req|
529
- expect(MultiJson.decode(req.body)["data"]).to eq('{"some":"data"}')
530
- }
531
- EM.stop
532
- }
533
- }
534
- end
535
- end
536
-
537
- [:get, :post].each do |verb|
538
- describe "##{verb}" do
539
- before :each do
540
- @url_regexp = %r{api-mt1.pusher.com}
541
- stub_request(verb, @url_regexp).
542
- to_return(:status => 200, :body => "{}")
543
- end
544
-
545
- let(:call_api) { @client.send(verb, '/path') }
546
-
547
- it "should use https by default" do
548
- call_api
549
- expect(WebMock).to have_requested(verb, %r{https://api-mt1.pusher.com/apps/20/path})
550
- end
551
-
552
- it "should use https if configured" do
553
- @client.encrypted = false
554
- call_api
555
- expect(WebMock).to have_requested(verb, %r{http://api-mt1.pusher.com})
556
- end
557
-
558
- it "should format the respose hash with symbols at first level" do
559
- stub_request(verb, @url_regexp).to_return({
560
- :status => 200,
561
- :body => MultiJson.encode({'something' => {'a' => 'hash'}})
562
- })
563
- expect(call_api).to eq({
564
- :something => {'a' => 'hash'}
565
- })
566
- end
567
-
568
- it "should catch all http exceptions and raise a Pusher::HTTPError wrapping the original error" do
569
- stub_request(verb, @url_regexp).to_raise(HTTPClient::TimeoutError)
570
-
571
- error = nil
572
- begin
573
- call_api
574
- rescue => e
575
- error = e
576
- end
577
-
578
- expect(error.class).to eq(Pusher::HTTPError)
579
- expect(error).to be_kind_of(Pusher::Error)
580
- expect(error.message).to eq('Exception from WebMock (HTTPClient::TimeoutError)')
581
- expect(error.original_error.class).to eq(HTTPClient::TimeoutError)
582
- end
583
-
584
- it "should raise Pusher::Error if call returns 400" do
585
- stub_request(verb, @url_regexp).to_return({:status => 400})
586
- expect { call_api }.to raise_error(Pusher::Error)
587
- end
588
-
589
- it "should raise AuthenticationError if pusher returns 401" do
590
- stub_request(verb, @url_regexp).to_return({:status => 401})
591
- expect { call_api }.to raise_error(Pusher::AuthenticationError)
592
- end
593
-
594
- it "should raise Pusher::Error if pusher returns 404" do
595
- stub_request(verb, @url_regexp).to_return({:status => 404})
596
- expect { call_api }.to raise_error(Pusher::Error, '404 Not found (/apps/20/path)')
597
- end
598
-
599
- it "should raise Pusher::Error if pusher returns 407" do
600
- stub_request(verb, @url_regexp).to_return({:status => 407})
601
- expect { call_api }.to raise_error(Pusher::Error, 'Proxy Authentication Required')
602
- end
603
-
604
- it "should raise Pusher::Error if pusher returns 413" do
605
- stub_request(verb, @url_regexp).to_return({:status => 413})
606
- expect { call_api }.to raise_error(Pusher::Error, 'Payload Too Large > 10KB')
607
- end
608
-
609
- it "should raise Pusher::Error if pusher returns 500" do
610
- stub_request(verb, @url_regexp).to_return({:status => 500, :body => "some error"})
611
- expect { call_api }.to raise_error(Pusher::Error, 'Unknown error (status code 500): some error')
612
- end
613
- end
614
- end
615
-
616
- describe "async calling without eventmachine" do
617
- [[:get, :get_async], [:post, :post_async]].each do |verb, method|
618
- describe "##{method}" do
619
- before :each do
620
- @url_regexp = %r{api-mt1.pusher.com}
621
- stub_request(verb, @url_regexp).
622
- to_return(:status => 200, :body => "{}")
623
- end
624
-
625
- let(:call_api) {
626
- @client.send(method, '/path').tap { |c|
627
- # Allow the async thread (inside httpclient) to run
628
- while !c.finished?
629
- sleep 0.01
630
- end
631
- }
632
- }
633
-
634
- it "should use https by default" do
635
- call_api
636
- expect(WebMock).to have_requested(verb, %r{https://api-mt1.pusher.com/apps/20/path})
637
- end
638
-
639
- it "should use http if configured" do
640
- @client.encrypted = false
641
- call_api
642
- expect(WebMock).to have_requested(verb, %r{http://api-mt1.pusher.com})
643
- end
644
-
645
- # Note that the raw httpclient connection object is returned and
646
- # the response isn't handled (by handle_response) in the normal way.
647
- it "should return a httpclient connection object" do
648
- connection = call_api
649
- expect(connection.finished?).to be_truthy
650
- response = connection.pop
651
- expect(response.status).to eq(200)
652
- expect(response.body.read).to eq("{}")
653
- end
654
- end
655
- end
656
- end
657
-
658
- describe "async calling with eventmachine" do
659
- [[:get, :get_async], [:post, :post_async]].each do |verb, method|
660
- describe "##{method}" do
661
- before :each do
662
- @url_regexp = %r{api-mt1.pusher.com}
663
- stub_request(verb, @url_regexp).
664
- to_return(:status => 200, :body => "{}")
665
- end
666
-
667
- let(:call_api) { @client.send(method, '/path') }
668
-
669
- it "should use https by default" do
670
- EM.run {
671
- call_api.callback {
672
- expect(WebMock).to have_requested(verb, %r{https://api-mt1.pusher.com/apps/20/path})
673
- EM.stop
674
- }
675
- }
676
- end
677
-
678
- it "should use http if configured" do
679
- EM.run {
680
- @client.encrypted = false
681
- call_api.callback {
682
- expect(WebMock).to have_requested(verb, %r{http://api-mt1.pusher.com})
683
- EM.stop
684
- }
685
- }
686
- end
687
-
688
- it "should format the respose hash with symbols at first level" do
689
- EM.run {
690
- stub_request(verb, @url_regexp).to_return({
691
- :status => 200,
692
- :body => MultiJson.encode({'something' => {'a' => 'hash'}})
693
- })
694
- call_api.callback { |response|
695
- expect(response).to eq({
696
- :something => {'a' => 'hash'}
697
- })
698
- EM.stop
699
- }
700
- }
701
- end
702
-
703
- it "should errback with Pusher::Error on unsuccessful response" do
704
- EM.run {
705
- stub_request(verb, @url_regexp).to_return({:status => 400})
706
-
707
- call_api.errback { |e|
708
- expect(e.class).to eq(Pusher::Error)
709
- EM.stop
710
- }.callback {
711
- fail
712
- }
713
- }
714
- end
715
- end
716
- end
717
- end
718
- end
719
- end
720
-
721
- describe 'configuring cluster' do
722
- it 'should allow clients to specify the cluster only with the default host' do
723
- client = Pusher::Client.new({
724
- :scheme => 'http',
725
- :cluster => 'eu',
726
- :port => 80
727
- })
728
- expect(client.host).to eq('api-eu.pusher.com')
729
- end
730
-
731
- it 'should always have host override any supplied cluster value' do
732
- client = Pusher::Client.new({
733
- :scheme => 'http',
734
- :host => 'api.staging.pusherapp.com',
735
- :cluster => 'eu',
736
- :port => 80
737
- })
738
- expect(client.host).to eq('api.staging.pusherapp.com')
739
- end
740
- end
741
- end