pusher 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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