pusher 1.3.1 → 1.4.2

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.
@@ -0,0 +1,56 @@
1
+ require 'sinatra'
2
+ require 'sinatra/cookies'
3
+ require 'sinatra/json'
4
+ require 'pusher'
5
+
6
+ # You can get these variables from http://dashboard.pusher.com
7
+ pusher = Pusher::Client.new(
8
+ app_id: 'your-app-id',
9
+ key: 'your-app-key',
10
+ secret: 'your-app-secret',
11
+ cluster: 'your-app-cluster'
12
+ )
13
+
14
+ set :public_folder, 'public'
15
+
16
+ get "/" do
17
+ redirect '/presence_channels.html'
18
+ end
19
+
20
+ # Emulate rails behaviour where this information would be stored in session
21
+ get '/signin' do
22
+ cookies[:user_id] = 'example_cookie'
23
+ 'Ok'
24
+ end
25
+
26
+ # Auth endpoint: https://pusher.com/docs/channels/server_api/authenticating-users
27
+ post '/pusher/auth' do
28
+ channel_data = {
29
+ user_id: 'example_user',
30
+ user_info: {
31
+ name: 'example_name',
32
+ email: 'example_email'
33
+ }
34
+ }
35
+
36
+ if cookies[:user_id] == 'example_cookie'
37
+ response = pusher.authenticate(params[:channel_name], params[:socket_id], channel_data)
38
+ json response
39
+ else
40
+ status 403
41
+ end
42
+ end
43
+
44
+ get '/pusher_trigger' do
45
+ channels = ['presence-channel-test'];
46
+
47
+ begin
48
+ pusher.trigger(channels, 'test-event', {
49
+ message: 'hello world'
50
+ })
51
+ rescue Pusher::Error => e
52
+ # (Pusher::AuthenticationError, Pusher::HTTPError, or Pusher::Error)
53
+ end
54
+
55
+ 'Triggered!'
56
+ end
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <head>
3
+ <title>Pusher Test</title>
4
+ <script src="https://js.pusher.com/5.0/pusher.min.js"></script>
5
+ <script>
6
+
7
+ // Enable pusher logging - don't include this in production
8
+ Pusher.logToConsole = true;
9
+
10
+ var pusher = new Pusher('your-app-key', {
11
+ cluster: 'your-app-cluster',
12
+ forceTLS: true,
13
+ authEndpoint: '/pusher/auth'
14
+ });
15
+
16
+ var channel = pusher.subscribe('presence-channel-test');
17
+ channel.bind('test-event', function(data) {
18
+ alert(JSON.stringify(data));
19
+ });
20
+ </script>
21
+ </head>
22
+ <body>
23
+ <h1>Pusher Test</h1>
24
+ <p>
25
+ Try publishing an event to channel <code>presence-channel-test</code>
26
+ with event name <code>test-event</code>.
27
+ </p>
28
+ </body>
@@ -32,7 +32,7 @@ module Pusher
32
32
  def_delegators :default_client, :scheme=, :host=, :port=, :app_id=, :key=, :secret=, :http_proxy=
33
33
  def_delegators :default_client, :notification_host=, :notification_scheme=
34
34
 
35
- def_delegators :default_client, :authentication_token, :url
35
+ def_delegators :default_client, :authentication_token, :url, :cluster
36
36
  def_delegators :default_client, :encrypted=, :url=, :cluster=
37
37
  def_delegators :default_client, :timeout=, :connect_timeout=, :send_timeout=, :receive_timeout=, :keep_alive_timeout=
38
38
 
@@ -86,6 +86,9 @@ module Pusher
86
86
 
87
87
  # Request info for a channel
88
88
  #
89
+ # @example Response
90
+ # [{:occupied=>true, :subscription_count => 12}]
91
+ #
89
92
  # @param info [Array] Array of attributes required (as lowercase strings)
90
93
  # @return [Hash] Hash of requested attributes for this channel
91
94
  # @raise [Pusher::Error] on invalid Pusher response - see the error message for more details
@@ -99,7 +102,7 @@ module Pusher
99
102
  # Only works on presence channels (see: http://pusher.com/docs/client_api_guide/client_presence_channels and https://pusher.com/docs/rest_api)
100
103
  #
101
104
  # @example Response
102
- # [{"id"=>"4"}]
105
+ # [{:id=>"4"}]
103
106
  #
104
107
  # @param params [Hash] Hash of parameters for the API - see REST API docs
105
108
  # @return [Hash] Array of user hashes for this channel
@@ -146,7 +149,7 @@ module Pusher
146
149
  # render :json => Pusher['private-my_channel'].authenticate(params[:socket_id])
147
150
  #
148
151
  # @example Presence channels
149
- # render :json => Pusher['private-my_channel'].authenticate(params[:socket_id], {
152
+ # render :json => Pusher['presence-my_channel'].authenticate(params[:socket_id], {
150
153
  # :user_id => current_user.id, # => required
151
154
  # :user_info => { # => optional - for example
152
155
  # :name => current_user.name,
@@ -171,6 +174,15 @@ module Pusher
171
174
  r
172
175
  end
173
176
 
177
+ def shared_secret(encryption_master_key)
178
+ return unless encryption_master_key
179
+
180
+ secret_string = @name + encryption_master_key
181
+ digest = OpenSSL::Digest::SHA256.new
182
+ digest << secret_string
183
+ digest.digest
184
+ end
185
+
174
186
  private
175
187
 
176
188
  def validate_socket_id(socket_id)
@@ -1,8 +1,10 @@
1
+ require 'base64'
2
+
1
3
  require 'pusher-signature'
2
4
 
3
5
  module Pusher
4
6
  class Client
5
- attr_accessor :scheme, :host, :port, :app_id, :key, :secret, :notification_host, :notification_scheme
7
+ attr_accessor :scheme, :host, :port, :app_id, :key, :secret, :notification_host, :notification_scheme, :encryption_master_key
6
8
  attr_reader :http_proxy, :proxy
7
9
  attr_writer :connect_timeout, :send_timeout, :receive_timeout,
8
10
  :keep_alive_timeout
@@ -12,6 +14,11 @@ module Pusher
12
14
  # Loads the configuration from an url in the environment
13
15
  def self.from_env(key = 'PUSHER_URL')
14
16
  url = ENV[key] || raise(ConfigurationError, key)
17
+ from_url(url)
18
+ end
19
+
20
+ # Loads the configuration from a url
21
+ def self.from_url(url)
15
22
  client = new
16
23
  client.url = url
17
24
  client
@@ -22,6 +29,12 @@ module Pusher
22
29
  :scheme => 'http',
23
30
  :port => 80,
24
31
  }
32
+
33
+ if options[:use_tls] || options[:encrypted]
34
+ default_options[:scheme] = "https"
35
+ default_options[:port] = 443
36
+ end
37
+
25
38
  merged_options = default_options.merge(options)
26
39
 
27
40
  if options.has_key?(:host)
@@ -44,6 +57,11 @@ module Pusher
44
57
  :scheme, :host, :port, :app_id, :key, :secret, :notification_host, :notification_scheme
45
58
  )
46
59
 
60
+ if options.has_key?(:encryption_master_key_base64)
61
+ @encryption_master_key =
62
+ Base64.decode64(options[:encryption_master_key_base64])
63
+ end
64
+
47
65
  @http_proxy = nil
48
66
  self.http_proxy = options[:http_proxy] if options[:http_proxy]
49
67
 
@@ -127,7 +145,13 @@ module Pusher
127
145
  @connect_timeout, @send_timeout, @receive_timeout = value, value, value
128
146
  end
129
147
 
130
- ## INTERACE WITH THE API ##
148
+ # Set an encryption_master_key to use with private-encrypted channels from
149
+ # a base64 encoded string.
150
+ def encryption_master_key_base64=(s)
151
+ @encryption_master_key = s ? Base64.decode64(s) : nil
152
+ end
153
+
154
+ ## INTERACT WITH THE API ##
131
155
 
132
156
  def resource(path)
133
157
  Resource.new(self, path)
@@ -351,7 +375,13 @@ module Pusher
351
375
  #
352
376
  def authenticate(channel_name, socket_id, custom_data = nil)
353
377
  channel_instance = channel(channel_name)
354
- channel_instance.authenticate(socket_id, custom_data)
378
+ r = channel_instance.authenticate(socket_id, custom_data)
379
+ if channel_name.match(/^private-encrypted-/)
380
+ r[:shared_secret] = Base64.strict_encode64(
381
+ channel_instance.shared_secret(encryption_master_key)
382
+ )
383
+ end
384
+ r
355
385
  end
356
386
 
357
387
  # @private Construct a net/http http client
@@ -402,10 +432,17 @@ module Pusher
402
432
  channels = Array(channels).map(&:to_s)
403
433
  raise Pusher::Error, "Too many channels (#{channels.length}), max 10" if channels.length > 10
404
434
 
435
+ encoded_data = if channels.any?{ |c| c.match(/^private-encrypted-/) } then
436
+ raise Pusher::Error, "Cannot trigger to multiple channels if any are encrypted" if channels.length > 1
437
+ encrypt(channels[0], encode_data(data))
438
+ else
439
+ encode_data(data)
440
+ end
441
+
405
442
  params.merge({
406
443
  name: event_name,
407
444
  channels: channels,
408
- data: encode_data(data),
445
+ data: encoded_data,
409
446
  })
410
447
  end
411
448
 
@@ -413,7 +450,11 @@ module Pusher
413
450
  {
414
451
  batch: events.map do |event|
415
452
  event.dup.tap do |e|
416
- e[:data] = encode_data(e[:data])
453
+ e[:data] = if e[:channel].match(/^private-encrypted-/) then
454
+ encrypt(e[:channel], encode_data(e[:data]))
455
+ else
456
+ encode_data(e[:data])
457
+ end
417
458
  end
418
459
  end
419
460
  }
@@ -425,8 +466,37 @@ module Pusher
425
466
  MultiJson.encode(data)
426
467
  end
427
468
 
469
+ # Encrypts a message with a key derived from the master key and channel
470
+ # name
471
+ def encrypt(channel_name, encoded_data)
472
+ raise ConfigurationError, :encryption_master_key unless @encryption_master_key
473
+
474
+ # Only now load rbnacl, so that people that aren't using it don't need to
475
+ # install libsodium
476
+ require_rbnacl
477
+
478
+ secret_box = RbNaCl::SecretBox.new(
479
+ channel(channel_name).shared_secret(@encryption_master_key)
480
+ )
481
+
482
+ nonce = RbNaCl::Random.random_bytes(secret_box.nonce_bytes)
483
+ ciphertext = secret_box.encrypt(nonce, encoded_data)
484
+
485
+ MultiJson.encode({
486
+ "nonce" => Base64::encode64(nonce),
487
+ "ciphertext" => Base64::encode64(ciphertext),
488
+ })
489
+ end
490
+
428
491
  def configured?
429
492
  host && scheme && key && secret && app_id
430
493
  end
494
+
495
+ def require_rbnacl
496
+ require 'rbnacl'
497
+ rescue LoadError => e
498
+ $stderr.puts "You don't have rbnacl installed in your application. Please add it to your Gemfile and run bundle install"
499
+ raise e
500
+ end
431
501
  end
432
502
  end
@@ -94,6 +94,8 @@ module Pusher
94
94
  raise Error, "404 Not found (#{@uri.path})"
95
95
  when 407
96
96
  raise Error, "Proxy Authentication Required"
97
+ when 413
98
+ raise Error, "Payload Too Large > 10KB"
97
99
  else
98
100
  raise Error, "Unknown error (status code #{status_code}): #{body}"
99
101
  end
@@ -1,3 +1,3 @@
1
1
  module Pusher
2
- VERSION = '1.3.1'
2
+ VERSION = '1.4.2'
3
3
  end
@@ -9,22 +9,23 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Pusher"]
10
10
  s.email = ["support@pusher.com"]
11
11
  s.homepage = "http://github.com/pusher/pusher-http-ruby"
12
- s.summary = %q{Pusher API client}
13
- s.description = %q{Wrapper for pusher.com REST api}
12
+ s.summary = %q{Pusher Channels API client}
13
+ s.description = %q{Wrapper for Pusher Channels REST api: : https://pusher.com/channels}
14
14
  s.license = "MIT"
15
15
 
16
- s.add_dependency "multi_json", "~> 1.0"
16
+ s.add_dependency "multi_json", "~> 1.15"
17
17
  s.add_dependency 'pusher-signature', "~> 0.1.8"
18
- s.add_dependency "httpclient", "~> 2.7"
18
+ s.add_dependency "httpclient", "~> 2.8"
19
19
  s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
20
20
 
21
- s.add_development_dependency "rspec", "~> 3.0"
22
- s.add_development_dependency "webmock"
23
- s.add_development_dependency "em-http-request", "~> 1.1.0"
24
- s.add_development_dependency "addressable", "=2.4.0"
25
- s.add_development_dependency "rake", "~> 10.4.2"
26
- s.add_development_dependency "rack", "~> 1.6.4"
27
- s.add_development_dependency "json", "~> 1.8.3"
21
+ s.add_development_dependency "rspec", "~> 3.9"
22
+ s.add_development_dependency "webmock", "~> 3.9"
23
+ s.add_development_dependency "em-http-request", "~> 1.1"
24
+ s.add_development_dependency "addressable", "~> 2.7"
25
+ s.add_development_dependency "rake", "~> 13.0"
26
+ s.add_development_dependency "rack", "~> 2.2"
27
+ s.add_development_dependency "json", "~> 2.3"
28
+ s.add_development_dependency "rbnacl", "~> 7.1"
28
29
 
29
30
  s.files = `git ls-files`.split("\n")
30
31
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -71,15 +71,17 @@ describe Pusher::Channel do
71
71
 
72
72
  describe '#info' do
73
73
  it "should call the Client#channel_info" do
74
- expect(@client).to receive(:get).with("/channels/mychannel", anything)
74
+ expect(@client).to receive(:get)
75
+ .with("/channels/mychannel", anything)
76
+ .and_return({:occupied => true, :subscription_count => 12})
75
77
  @channel = @client['mychannel']
76
78
  @channel.info
77
79
  end
78
80
 
79
81
  it "should assemble the requested attributes into the info option" do
80
- expect(@client).to receive(:get).with(anything, {
81
- :info => "user_count,connection_count"
82
- })
82
+ expect(@client).to receive(:get)
83
+ .with(anything, {:info => "user_count,connection_count"})
84
+ .and_return({:occupied => true, :subscription_count => 12, :user_count => 12})
83
85
  @channel = @client['presence-foo']
84
86
  @channel.info(%w{user_count connection_count})
85
87
  end
@@ -165,4 +167,23 @@ describe Pusher::Channel do
165
167
  }.to raise_error Pusher::Error
166
168
  end
167
169
  end
170
+
171
+ describe `#shared_secret` do
172
+ before(:each) do
173
+ @channel.instance_variable_set(:@name, 'private-encrypted-1')
174
+ end
175
+
176
+ it 'should return a shared_secret based on the channel name and encryption master key' do
177
+ key = '3W1pfB/Etr+ZIlfMWwZP3gz8jEeCt4s2pe6Vpr+2c3M='
178
+ shared_secret = @channel.shared_secret(key)
179
+ expect(Base64.strict_encode64(shared_secret)).to eq(
180
+ "6zeEp/chneRPS1cbK/hGeG860UhHomxSN6hTgzwT20I="
181
+ )
182
+ end
183
+
184
+ it 'should return nil if missing encryption master key' do
185
+ shared_secret = @channel.shared_secret(nil)
186
+ expect(shared_secret).to be_nil
187
+ end
188
+ end
168
189
  end
@@ -1,7 +1,12 @@
1
- require 'spec_helper'
1
+ require 'base64'
2
2
 
3
+ require 'rbnacl'
3
4
  require 'em-http'
4
5
 
6
+ require 'spec_helper'
7
+
8
+ encryption_master_key = RbNaCl::Random.random_bytes(32)
9
+
5
10
  describe Pusher do
6
11
  # The behaviour should be the same when using the Client object, or the
7
12
  # 'global' client delegated through the Pusher class
@@ -87,19 +92,53 @@ describe Pusher do
87
92
  expect(@client.host).to eq('api.staging.pusherapp.com')
88
93
  end
89
94
 
90
- it 'should get override the url configuration if it comes after' do
95
+ it 'should override the url configuration if it comes after' do
91
96
  @client.url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
92
97
  @client.cluster = 'eu'
93
98
  expect(@client.host).to eq('api-eu.pusher.com')
94
99
  end
95
100
 
96
- it 'should overrie by the host configuration if it comes after' do
101
+ it 'should override the host configuration if it comes after' do
97
102
  @client.host = 'api.staging.pusher.com'
98
103
  @client.cluster = 'eu'
99
104
  expect(@client.host).to eq('api-eu.pusher.com')
100
105
  end
101
106
  end
102
107
 
108
+ describe 'configuring TLS' do
109
+ it 'should set port and scheme if "use_tls" enabled' do
110
+ client = Pusher::Client.new({
111
+ :use_tls => true,
112
+ })
113
+ expect(client.scheme).to eq('https')
114
+ expect(client.port).to eq(443)
115
+ end
116
+
117
+ it 'should set port and scheme if "encrypted" enabled' do
118
+ client = Pusher::Client.new({
119
+ :encrypted => true,
120
+ })
121
+ expect(client.scheme).to eq('https')
122
+ expect(client.port).to eq(443)
123
+ end
124
+
125
+ it 'should use non-TLS port and scheme if "encrypted" or "use_tls" are not set' do
126
+ client = Pusher::Client.new
127
+ expect(client.scheme).to eq('http')
128
+ expect(client.port).to eq(80)
129
+ end
130
+
131
+ it 'should override port if "use_tls" option set but a different port is specified' do
132
+ client = Pusher::Client.new({
133
+ :use_tls => true,
134
+ :port => 8443
135
+ })
136
+ expect(client.scheme).to eq('https')
137
+ expect(client.port).to eq(8443)
138
+ end
139
+
140
+ end
141
+
103
142
  describe 'configuring a http proxy' do
104
143
  it "should be possible to configure everything by setting the http_proxy" do
105
144
  @client.http_proxy = 'http://someuser:somepassword@proxy.host.com:8080'
@@ -109,6 +148,10 @@ describe Pusher do
109
148
  end
110
149
 
111
150
  describe 'configuring from env' do
151
+ after do
152
+ ENV['PUSHER_URL'] = nil
153
+ end
154
+
112
155
  it "works" do
113
156
  url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
114
157
  ENV['PUSHER_URL'] = url
@@ -118,7 +161,27 @@ describe Pusher do
118
161
  expect(client.secret).to eq("somesecret")
119
162
  expect(client.app_id).to eq("87")
120
163
  expect(client.url.to_s).to eq("http://api.staging.pusherapp.com:8080/apps/87")
121
- ENV['PUSHER_URL'] = nil
164
+ end
165
+ end
166
+
167
+ describe 'configuring from url' do
168
+ it "works" do
169
+ url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
170
+
171
+ client = Pusher::Client.from_url(url)
172
+ expect(client.key).to eq("somekey")
173
+ expect(client.secret).to eq("somesecret")
174
+ expect(client.app_id).to eq("87")
175
+ expect(client.url.to_s).to eq("http://api.staging.pusherapp.com:8080/apps/87")
176
+ end
177
+ end
178
+
179
+ describe 'can set encryption_master_key_base64' do
180
+ it "sets encryption_master_key" do
181
+ @client.encryption_master_key_base64 =
182
+ Base64.strict_encode64(encryption_master_key)
183
+
184
+ expect(@client.encryption_master_key).to eq(encryption_master_key)
122
185
  end
123
186
  end
124
187
 
@@ -127,6 +190,8 @@ describe Pusher do
127
190
  @client.app_id = '20'
128
191
  @client.key = '12345678900000001'
129
192
  @client.secret = '12345678900000001'
193
+ @client.encryption_master_key_base64 =
194
+ Base64.strict_encode64(encryption_master_key)
130
195
  end
131
196
 
132
197
  describe '#[]' do
@@ -211,6 +276,19 @@ describe Pusher do
211
276
  })
212
277
  end
213
278
 
279
+ it 'should include a shared_secret if the private-encrypted channel' do
280
+ allow(MultiJson).to receive(:encode).with(@custom_data).and_return 'a json string'
281
+ @client.instance_variable_set(:@encryption_master_key, '3W1pfB/Etr+ZIlfMWwZP3gz8jEeCt4s2pe6Vpr+2c3M=')
282
+
283
+ response = @client.authenticate('private-encrypted-test_channel', '1.1', @custom_data)
284
+
285
+ expect(response).to eq({
286
+ :auth => "12345678900000001:#{hmac(@client.secret, "1.1:private-encrypted-test_channel:a json string")}",
287
+ :shared_secret => "o0L3QnIovCeRC8KTD8KBRlmi31dGzHVS2M93uryqDdw=",
288
+ :channel_data => 'a json string'
289
+ })
290
+ end
291
+
214
292
  end
215
293
 
216
294
  describe '#trigger' do
@@ -272,6 +350,46 @@ describe Pusher do
272
350
  }
273
351
  end
274
352
  end
353
+
354
+ it "should fail to publish to encrypted channels when missing key" do
355
+ @client.encryption_master_key_base64 = nil
356
+ expect {
357
+ @client.trigger('private-encrypted-channel', 'event', {'some' => 'data'})
358
+ }.to raise_error(Pusher::ConfigurationError)
359
+ expect(WebMock).not_to have_requested(:post, @api_path)
360
+ end
361
+
362
+ it "should fail to publish to multiple channels if one is encrypted" do
363
+ expect {
364
+ @client.trigger(
365
+ ['private-encrypted-channel', 'some-other-channel'],
366
+ 'event',
367
+ {'some' => 'data'},
368
+ )
369
+ }.to raise_error(Pusher::Error)
370
+ expect(WebMock).not_to have_requested(:post, @api_path)
371
+ end
372
+
373
+ it "should encrypt publishes to encrypted channels" do
374
+ @client.trigger(
375
+ 'private-encrypted-channel',
376
+ 'event',
377
+ {'some' => 'data'},
378
+ )
379
+
380
+ expect(WebMock).to have_requested(:post, @api_path).with { |req|
381
+ data = MultiJson.decode(MultiJson.decode(req.body)["data"])
382
+
383
+ key = RbNaCl::Hash.sha256(
384
+ 'private-encrypted-channel' + encryption_master_key
385
+ )
386
+
387
+ expect(MultiJson.decode(RbNaCl::SecretBox.new(key).decrypt(
388
+ Base64.decode64(data["nonce"]),
389
+ Base64.decode64(data["ciphertext"]),
390
+ ))).to eq({ 'some' => 'data' })
391
+ }
392
+ end
275
393
  end
276
394
 
277
395
  describe '#trigger_batch' do
@@ -303,6 +421,55 @@ describe Pusher do
303
421
  )
304
422
  }
305
423
  end
424
+
425
+ it "should fail to publish to encrypted channels when missing key" do
426
+ @client.encryption_master_key_base64 = nil
427
+ expect {
428
+ @client.trigger_batch(
429
+ {
430
+ channel: 'private-encrypted-channel',
431
+ name: 'event',
432
+ data: {'some' => 'data'},
433
+ },
434
+ {channel: 'mychannel', name: 'event', data: 'already encoded'},
435
+ )
436
+ }.to raise_error(Pusher::ConfigurationError)
437
+ expect(WebMock).not_to have_requested(:post, @api_path)
438
+ end
439
+
440
+ it "should encrypt publishes to encrypted channels" do
441
+ @client.trigger_batch(
442
+ {
443
+ channel: 'private-encrypted-channel',
444
+ name: 'event',
445
+ data: {'some' => 'data'},
446
+ },
447
+ {channel: 'mychannel', name: 'event', data: 'already encoded'},
448
+ )
449
+
450
+ expect(WebMock).to have_requested(:post, @api_path).with { |req|
451
+ batch = MultiJson.decode(req.body)["batch"]
452
+ expect(batch.length).to eq(2)
453
+
454
+ expect(batch[0]["channel"]).to eq("private-encrypted-channel")
455
+ expect(batch[0]["name"]).to eq("event")
456
+
457
+ data = MultiJson.decode(batch[0]["data"])
458
+
459
+ key = RbNaCl::Hash.sha256(
460
+ 'private-encrypted-channel' + encryption_master_key
461
+ )
462
+
463
+ expect(MultiJson.decode(RbNaCl::SecretBox.new(key).decrypt(
464
+ Base64.decode64(data["nonce"]),
465
+ Base64.decode64(data["ciphertext"]),
466
+ ))).to eq({ 'some' => 'data' })
467
+
468
+ expect(batch[1]["channel"]).to eq("mychannel")
469
+ expect(batch[1]["name"]).to eq("event")
470
+ expect(batch[1]["data"]).to eq("already encoded")
471
+ }
472
+ end
306
473
  end
307
474
 
308
475
  describe '#trigger_async' do
@@ -415,6 +582,11 @@ describe Pusher do
415
582
  expect { call_api }.to raise_error(Pusher::Error, 'Proxy Authentication Required')
416
583
  end
417
584
 
585
+ it "should raise Pusher::Error if pusher returns 413" do
586
+ stub_request(verb, @url_regexp).to_return({:status => 413})
587
+ expect { call_api }.to raise_error(Pusher::Error, 'Payload Too Large > 10KB')
588
+ end
589
+
418
590
  it "should raise Pusher::Error if pusher returns 500" do
419
591
  stub_request(verb, @url_regexp).to_return({:status => 500, :body => "some error"})
420
592
  expect { call_api }.to raise_error(Pusher::Error, 'Unknown error (status code 500): some error')
@@ -612,4 +784,3 @@ describe Pusher do
612
784
  end
613
785
  end
614
786
  end
615
-