ably 0.7.1 → 0.7.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.
@@ -9,7 +9,7 @@ module Ably
9
9
  #
10
10
  FALLBACK_HOSTS = %w(A.ably-realtime.com B.ably-realtime.com C.ably-realtime.com D.ably-realtime.com E.ably-realtime.com)
11
11
  INTERNET_CHECK = {
12
- url: 'http://internet-up.ably.io/is-the-internet-up.txt', #ably-realtime.com
12
+ url: '//internet-up.ably-realtime.com/is-the-internet-up.txt',
13
13
  ok_text: 'yes'
14
14
  }
15
15
  end
@@ -111,12 +111,16 @@ module Ably
111
111
 
112
112
  # Retrieve the stats for the application
113
113
  #
114
- # @yield [Array] An Array of hashes representing the stats
114
+ # @param (see Ably::Rest::Client#stats)
115
+ # @option options (see Ably::Rest::Client#stats)
116
+ #
117
+ # @yield [Ably::Models::PaginatedResource<Ably::Models::Stat>] An Array of Stats
118
+ #
115
119
  # @return [EventMachine::Deferrable]
116
120
  #
117
- def stats(params = {}, &success_callback)
121
+ def stats(options = {}, &success_callback)
118
122
  async_wrap(success_callback) do
119
- rest_client.stats(params)
123
+ rest_client.stats(options)
120
124
  end
121
125
  end
122
126
 
@@ -119,7 +119,7 @@ module Ably::Realtime
119
119
  if protocol_message.connection_key && (protocol_message.connection_key != connection.key)
120
120
  logger.debug "New connection ID set to #{protocol_message.connection_id} with connection key #{protocol_message.connection_key}"
121
121
  detach_attached_channels protocol_message.error if protocol_message.error
122
- connection.update_connection_id_and_key protocol_message.connection_id, protocol_message.connection_key
122
+ connection.configure_new protocol_message.connection_id, protocol_message.connection_key, protocol_message.connection_serial
123
123
  end
124
124
 
125
125
  if protocol_message.has_connection_serial?
@@ -99,7 +99,7 @@ module Ably
99
99
  # @api public
100
100
  def initialize(client)
101
101
  @client = client
102
- @serial = -1
102
+ @client_serial = -1
103
103
  @__outgoing_message_queue__ = []
104
104
  @__pending_message_queue__ = []
105
105
 
@@ -182,7 +182,7 @@ module Ably
182
182
  # @api private
183
183
  def internet_up?
184
184
  EventMachine::DefaultDeferrable.new.tap do |deferrable|
185
- EventMachine::HttpRequest.new(Ably::INTERNET_CHECK.fetch(:url)).get.tap do |http|
185
+ EventMachine::HttpRequest.new("http#{'s' if client.use_tls?}:#{Ably::INTERNET_CHECK.fetch(:url)}").get.tap do |http|
186
186
  http.errback do
187
187
  yield false if block_given?
188
188
  deferrable.fail
@@ -202,12 +202,17 @@ module Ably
202
202
  "#{key}:#{serial}" if connection_resumable?
203
203
  end
204
204
 
205
- # Configure the current connection ID and connection key
205
+ # Following a new connection being made, resumed or recovered, the connection ID, connection key
206
+ # and message serial need to match the details provided by the server.
207
+ #
206
208
  # @return [void]
207
209
  # @api private
208
- def update_connection_id_and_key(connection_id, connection_key)
209
- @id = connection_id
210
- @key = connection_key
210
+ def configure_new(connection_id, connection_key, connection_serial)
211
+ @id = connection_id
212
+ @key = connection_key
213
+ @client_serial = connection_serial
214
+
215
+ update_connection_serial connection_serial
211
216
  end
212
217
 
213
218
  # Store last received connection serial so that the connection can be resumed from the last known point-in-time
@@ -362,6 +367,17 @@ module Ably
362
367
  private :change_state
363
368
 
364
369
  private
370
+
371
+ # The client serial is incremented for every message that is published that requires an ACK.
372
+ # Note that this is different to the connection serial that contains the last known serial number
373
+ # received from the server.
374
+ #
375
+ # A client serial number therefore does not guarantee a message has been received, only sent.
376
+ # A connection serial guarantees the server has received the message and is thus used for connection
377
+ # recovery and resumes.
378
+ # @return [Integer] starting at -1 indicating no messages sent, 0 when the first message is sent
379
+ attr_reader :client_serial
380
+
365
381
  def create_pub_sub_message_bus
366
382
  Ably::Util::PubSub.new(
367
383
  coerce_into: Proc.new do |event|
@@ -380,11 +396,11 @@ module Ably
380
396
  end
381
397
 
382
398
  def add_message_serial_to(protocol_message)
383
- @serial += 1
384
- protocol_message[:msgSerial] = serial
399
+ @client_serial += 1
400
+ protocol_message[:msgSerial] = client_serial
385
401
  yield
386
402
  rescue StandardError => e
387
- @serial -= 1
403
+ @client_serial -= 1
388
404
  raise e
389
405
  end
390
406
 
@@ -10,8 +10,8 @@ module Ably::Realtime
10
10
  class ConnectionManager
11
11
  # Configuration for automatic recovery of failed connection attempts
12
12
  CONNECT_RETRY_CONFIG = {
13
- disconnected: { retry_every: 0.5, max_time_in_state: 10 },
14
- suspended: { retry_every: 5, max_time_in_state: 60 }
13
+ disconnected: { retry_every: 15, max_time_in_state: 120 },
14
+ suspended: { retry_every: 120, max_time_in_state: Float::INFINITY }
15
15
  }.freeze
16
16
 
17
17
  # Time to wait following a connection state request before it's considered a failure
@@ -139,20 +139,33 @@ module Ably
139
139
  channels.get(name, channel_options)
140
140
  end
141
141
 
142
- # Retrieve the stats for the application
142
+ # Retrieve the Stats for the application
143
143
  #
144
- # @return [Array] An Array of hashes representing the stats
145
- def stats(params = {})
146
- default_params = {
144
+ # @param [Hash] options the options for the stats request
145
+ # @option options [Integer,Time] :start Time or millisecond since epoch
146
+ # @option options [Integer,Time] :end Time or millisecond since epoch
147
+ # @option options [Symbol] :direction `:forwards` or `:backwards`
148
+ # @option options [Integer] :limit Maximum number of stats to retrieve up to 10,000
149
+ # @option options [Symbol] :by `:minute`, `:hour`, `:day` or `:month`. Defaults to `:minute`
150
+ #
151
+ # @return [Ably::Models::PaginatedResource<Ably::Models::Stat>] An Array of Stats
152
+ #
153
+ def stats(options = {})
154
+ options = {
147
155
  :direction => :forwards,
148
156
  :by => :minute
157
+ }.merge(options)
158
+
159
+ [:start, :end].each { |option| options[option] = as_since_epoch(options[option]) if options.has_key?(option) }
160
+
161
+ paginated_options = {
162
+ coerce_into: 'Ably::Models::Stat'
149
163
  }
150
164
 
151
- response = get("/stats", default_params.merge(params))
165
+ url = '/stats'
166
+ response = get(url, options)
152
167
 
153
- response.body.map do |stat|
154
- IdiomaticRubyWrapper(stat)
155
- end
168
+ Ably::Models::PaginatedResource.new(response, url, self, paginated_options)
156
169
  end
157
170
 
158
171
  # Retrieve the Ably service time
@@ -42,7 +42,7 @@ module Ably
42
42
  end
43
43
 
44
44
  rescue StandardError
45
- "Error displaying body: (as hex) '#{readable_body(env.body)}'"
45
+ readable_body(env.body)
46
46
  end
47
47
 
48
48
  def readable_body(body)
data/lib/ably/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ably
2
- VERSION = '0.7.1'
2
+ VERSION = '0.7.2'
3
3
  end
@@ -61,7 +61,7 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
61
61
 
62
62
  context 'with lots of messages published with a single client and channel' do
63
63
  let(:messages_sent) { 30 }
64
- let(:rate_per_second) { 4 }
64
+ let(:rate_per_second) { 10 }
65
65
  let(:limit) { 15 }
66
66
 
67
67
  def ensure_message_history_direction_and_paging_is_correct(direction)
@@ -103,7 +103,7 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
103
103
  end
104
104
  end
105
105
 
106
- context 'in multiple ProtocolMessages', em_timeout: (30 / 4) + 20 do
106
+ context 'in multiple ProtocolMessages', em_timeout: (30 / 10) + 5 do
107
107
  it 'retrieves limited history forwards with pagination' do
108
108
  messages_sent.times do |index|
109
109
  EventMachine.add_timer(index.to_f / rate_per_second) do
@@ -127,7 +127,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
127
127
  end
128
128
 
129
129
  context 'when connection state is :suspended' do
130
- it 'enters the failed state after multiple attempts' do
130
+ it 'enters the failed state after multiple attempts if the max_time_in_state is set' do
131
131
  connection.on(:connected) { raise 'Connection should not have reached :connected state' }
132
132
 
133
133
  connection.once(:suspended) do
@@ -397,7 +397,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
397
397
  context 'when failing to resume because the connection_key is not or no longer valid' do
398
398
  def kill_connection_transport_and_prevent_valid_resume
399
399
  connection.transport.close_connection_after_writing
400
- connection.update_connection_id_and_key '0123456789abcdef', '0123456789abcdef' # force the resume connection key to be invalid
400
+ connection.configure_new '0123456789abcdef', '0123456789abcdef', -1 # force the resume connection key to be invalid
401
401
  end
402
402
 
403
403
  it 'updates the connection_id and connection_key' do
@@ -198,7 +198,6 @@ describe Ably::Realtime::Connection, :event_machine do
198
198
  end
199
199
  end
200
200
  end
201
-
202
201
  end
203
202
 
204
203
  context 'initialization state changes' do
@@ -330,6 +329,48 @@ describe Ably::Realtime::Connection, :event_machine do
330
329
  end
331
330
  end
332
331
 
332
+ describe '#serial connection serial' do
333
+ let(:channel) { client.channel(random_str) }
334
+
335
+ it 'is set to -1 when a new connection is opened' do
336
+ connection.connect do
337
+ expect(connection.serial).to eql(-1)
338
+ stop_reactor
339
+ end
340
+ end
341
+
342
+ context 'when a message is sent but the ACK has not yet been received' do
343
+
344
+ it 'the sent message msgSerial is 0 but the connection serial remains at -1' do
345
+ channel.attach do
346
+ connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
347
+ connection.__outgoing_protocol_msgbus__.unsubscribe
348
+ expect(protocol_message['msgSerial']).to eql(0)
349
+ expect(connection.serial).to eql(-1)
350
+ stop_reactor
351
+ end
352
+ channel.publish('event', 'data')
353
+ end
354
+ end
355
+ end
356
+
357
+ it 'is set to 0 when a message sent ACK is received' do
358
+ channel.publish('event', 'data') do
359
+ expect(connection.serial).to eql(0)
360
+ stop_reactor
361
+ end
362
+ end
363
+
364
+ it 'is set to 1 when the second message sent ACK is received' do
365
+ channel.publish('event', 'data') do
366
+ channel.publish('event', 'data') do
367
+ expect(connection.serial).to eql(1)
368
+ stop_reactor
369
+ end
370
+ end
371
+ end
372
+ end
373
+
333
374
  context '#close' do
334
375
  it 'returns a Deferrable' do
335
376
  connection.connect do
@@ -491,7 +532,7 @@ describe Ably::Realtime::Connection, :event_machine do
491
532
  let(:states) { Hash.new }
492
533
  let(:client_options) { default_options.merge(log_level: :none) }
493
534
 
494
- it 'is composed of connection id and serial that is kept up to date with each message sent' do
535
+ it 'is composed of connection id and serial that is kept up to date with each message ACK received' do
495
536
  connection.on(:connected) do
496
537
  expected_serial = -1
497
538
  expect(connection.id).to_not be_nil
@@ -681,6 +722,30 @@ describe Ably::Realtime::Connection, :event_machine do
681
722
  stop_reactor
682
723
  end
683
724
 
725
+ context 'internet up URL protocol' do
726
+ let(:http_request) { double('EventMachine::HttpRequest', get: EventMachine::DefaultDeferrable.new) }
727
+
728
+ context 'when using TLS for the connection' do
729
+ let(:client_options) { default_options.merge(tls: true) }
730
+
731
+ it 'uses TLS for the Internet check to https://internet-up.ably-realtime.com/is-the-internet-up.txt' do
732
+ expect(EventMachine::HttpRequest).to receive(:new).with('https://internet-up.ably-realtime.com/is-the-internet-up.txt').and_return(http_request)
733
+ connection.internet_up?
734
+ stop_reactor
735
+ end
736
+ end
737
+
738
+ context 'when using a non-secured connection' do
739
+ let(:client_options) { default_options.merge(tls: false, use_token_auth: true) }
740
+
741
+ it 'uses TLS for the Internet check to http://internet-up.ably-realtime.com/is-the-internet-up.txt' do
742
+ expect(EventMachine::HttpRequest).to receive(:new).with('http://internet-up.ably-realtime.com/is-the-internet-up.txt').and_return(http_request)
743
+ connection.internet_up?
744
+ stop_reactor
745
+ end
746
+ end
747
+ end
748
+
684
749
  context 'when the Internet is up' do
685
750
  it 'calls the block with true' do
686
751
  connection.internet_up? do |result|
@@ -698,7 +763,7 @@ describe Ably::Realtime::Connection, :event_machine do
698
763
 
699
764
  context 'when the Internet is down' do
700
765
  before do
701
- stub_const 'Ably::INTERNET_CHECK', { url: 'http://does.not.exist.com', ok_text: 'no.way.this.will.match' }
766
+ stub_const 'Ably::INTERNET_CHECK', { url: '//does.not.exist.com', ok_text: 'no.way.this.will.match' }
702
767
  end
703
768
 
704
769
  it 'calls the block with false' do
@@ -7,9 +7,9 @@ describe Ably::Realtime::Client, '#stats', :event_machine do
7
7
  end
8
8
 
9
9
  describe 'fetching stats' do
10
- it 'should return a Hash' do
10
+ it 'should return a PaginatedResource' do
11
11
  client.stats do |stats|
12
- expect(stats).to be_a(Array)
12
+ expect(stats).to be_a(Ably::Models::PaginatedResource)
13
13
  stop_reactor
14
14
  end
15
15
  end
@@ -59,16 +59,18 @@ describe Ably::Auth do
59
59
  let(:ttl) { 30 * 60 }
60
60
  let(:capability) { { :foo => ['publish'] } }
61
61
 
62
- it 'returns the requested token' do
63
- actual_token = auth.request_token(
62
+ let(:token) do
63
+ auth.request_token(
64
64
  ttl: ttl,
65
65
  capability: capability
66
66
  )
67
+ end
67
68
 
68
- expect(actual_token.id).to match(/^#{app_id}\.[\w-]+$/)
69
- expect(actual_token.key_id).to match(/^#{key_id}$/)
70
- expect(actual_token.issued_at).to be_within(2).of(Time.now)
71
- expect(actual_token.expires_at).to be_within(2).of(Time.now + ttl)
69
+ it 'returns a valid requested token in the expected format with valid issued_at and expires_at attributes' do
70
+ expect(token.id).to match(/^#{app_id}\.[\w-]+$/)
71
+ expect(token.key_id).to match(/^#{key_id}$/)
72
+ expect(token.issued_at).to be_within(2).of(Time.now)
73
+ expect(token.expires_at).to be_within(2).of(Time.now + ttl)
72
74
  end
73
75
 
74
76
  %w(client_id capability nonce timestamp ttl).each do |option|
@@ -60,7 +60,7 @@ describe Ably::Models::MessageEncoders do
60
60
  context 'with JSON data' do
61
61
  let(:published_data) { json_data }
62
62
 
63
- it 'stringifies the JSON and sets the json encoding type' do
63
+ it 'stringifies the JSON and sets the encoding attribute to "json"' do
64
64
  on_publish do |encoding, encoded_data|
65
65
  expect(encoding).to eql('json')
66
66
  expect(encoded_data).to eql(JSON.dump(published_data))
@@ -75,7 +75,7 @@ describe Ably::Models::MessageEncoders do
75
75
  context 'with UTF-8 data' do
76
76
  let(:published_data) { utf_8_data }
77
77
 
78
- it 'applies utf-8 and cipher encoding' do
78
+ it 'applies utf-8 and cipher encoding and sets the encoding attribute to "utf-8/cipher+aes-128-cbc"' do
79
79
  on_publish do |encoding, encoded_data|
80
80
  expect(encoding).to eql('utf-8/cipher+aes-128-cbc')
81
81
  expect(decrypted(encoded_data)).to eql(published_data)
@@ -86,7 +86,7 @@ describe Ably::Models::MessageEncoders do
86
86
  context 'with binary data' do
87
87
  let(:published_data) { binary_data }
88
88
 
89
- it 'applies cipher encoding' do
89
+ it 'applies cipher encoding and sets the encoding attribute to "cipher+aes-128-cbc"' do
90
90
  on_publish do |encoding, encoded_data|
91
91
  expect(encoding).to eql('cipher+aes-128-cbc')
92
92
  expect(decrypted(encoded_data)).to eql(published_data)
@@ -97,7 +97,7 @@ describe Ably::Models::MessageEncoders do
97
97
  context 'with JSON data' do
98
98
  let(:published_data) { json_data }
99
99
 
100
- it 'applies json, utf-8 and cipher encoding' do
100
+ it 'applies json, utf-8 and cipher encoding and sets the encoding attribute to "json/utf-8/cipher+aes-128-cbc"' do
101
101
  on_publish do |encoding, encoded_data|
102
102
  expect(encoding).to eql('json/utf-8/cipher+aes-128-cbc')
103
103
  expect(decrypted(encoded_data)).to eql(JSON.dump(published_data))
@@ -125,7 +125,7 @@ describe Ably::Models::MessageEncoders do
125
125
  context 'with binary data' do
126
126
  let(:published_data) { binary_data }
127
127
 
128
- it 'applies a base64 encoding' do
128
+ it 'applies a base64 encoding and sets the encoding attribute to "base64"' do
129
129
  on_publish do |encoding, encoded_data|
130
130
  expect(encoding).to eql('base64')
131
131
  expect(Base64.decode64(encoded_data)).to eql(published_data)
@@ -136,7 +136,7 @@ describe Ably::Models::MessageEncoders do
136
136
  context 'with JSON data' do
137
137
  let(:published_data) { json_data }
138
138
 
139
- it 'stringifies the JSON and sets the json encoding type' do
139
+ it 'stringifies the JSON and sets the encoding attribute to "json"' do
140
140
  on_publish do |encoding, encoded_data|
141
141
  expect(encoding).to eql('json')
142
142
  expect(encoded_data).to eql(JSON.dump(published_data))
@@ -151,7 +151,7 @@ describe Ably::Models::MessageEncoders do
151
151
  context 'with UTF-8 data' do
152
152
  let(:published_data) { utf_8_data }
153
153
 
154
- it 'applies utf-8, cipher and base64 encodings' do
154
+ it 'applies utf-8, cipher and base64 encodings and sets the encoding attribute to "utf-8/cipher+aes-128-cbc/base64"' do
155
155
  on_publish do |encoding, encoded_data|
156
156
  expect(encoding).to eql('utf-8/cipher+aes-128-cbc/base64')
157
157
  expect(decrypted(encoded_data, base64: true)).to eql(published_data)
@@ -162,7 +162,7 @@ describe Ably::Models::MessageEncoders do
162
162
  context 'with binary data' do
163
163
  let(:published_data) { binary_data }
164
164
 
165
- it 'applies cipher and base64 encoding' do
165
+ it 'applies cipher and base64 encoding and sets the encoding attribute to "utf-8/cipher+aes-128-cbc/base64"' do
166
166
  on_publish do |encoding, encoded_data|
167
167
  expect(encoding).to eql('cipher+aes-128-cbc/base64')
168
168
  expect(decrypted(encoded_data, base64: true)).to eql(published_data)
@@ -173,7 +173,7 @@ describe Ably::Models::MessageEncoders do
173
173
  context 'with JSON data' do
174
174
  let(:published_data) { json_data }
175
175
 
176
- it 'applies json, utf-8, cipher and base64 encoding' do
176
+ it 'applies json, utf-8, cipher and base64 encoding and sets the encoding attribute to "json/utf-8/cipher+aes-128-cbc/base64"' do
177
177
  on_publish do |encoding, encoded_data|
178
178
  expect(encoding).to eql('json/utf-8/cipher+aes-128-cbc/base64')
179
179
  expect(decrypted(encoded_data, base64: true)).to eql(JSON.dump(published_data))
@@ -21,8 +21,7 @@ describe Ably::Rest::Presence do
21
21
  describe '#get' do
22
22
  before(:context) do
23
23
  # When this test is run as a part of a test suite, the presence data injected in the test app may have expired
24
- WebMock.disable!
25
- TestApp.reload
24
+ reload_test_app
26
25
  end
27
26
 
28
27
  let(:channel) { client.channel('persisted:presence_fixtures') }
@@ -54,8 +53,7 @@ describe Ably::Rest::Presence do
54
53
  describe '#history' do
55
54
  before(:context) do
56
55
  # When this test is run as a part of a test suite, the presence data injected in the test app may have expired
57
- WebMock.disable!
58
- TestApp.reload
56
+ reload_test_app
59
57
  end
60
58
 
61
59
  let(:channel) { client.channel('persisted:presence_fixtures') }