pusher 1.3.1 → 1.4.2

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